gomatch

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2021 License: MIT Imports: 7 Imported by: 0

README

Gomatch

Build Status codecov GoDoc Go Report Card

Library created for testing JSON against patterns. The goal was to be able to validate JSON focusing only on parts essential in given test case so tests are more expressive and less fragile. It can be used with both unit tests and functional tests.

When used with Gherkin driven BDD tests it makes scenarios more compact and readable. See Gherkin example

Contests

Installation

go get github.com/jfilipczyk/gomatch

Basic usage


actual := `
{
  "id": 351,
  "name": "John Smith",
  "address": {
    "city": "Boston"
  }
}
`
expected := `
{
  "id": "@number@",
  "name": "John Smith",
  "address": {
    "city": "@string@"
  }
}
`

m := gomatch.NewDefaultJSONMatcher()
ok, err := m.Match(expected, actual)
if ok {
  fmt.Printf("actual JSON matches expected JSON")
} else {
  fmt.Printf("actual JSON does not match expected JSON: %s", err.Error())
}

Available patterns

  • @string@
  • @number@
  • @bool@
  • @array@
  • @uuid@
  • @email@
  • @wildcard@
  • @...@ - unbounded array or object
Unbounded pattern

It can be used at the end of an array to allow any extra array elements:

[
  "John Smith",
  "Joe Doe",
  "@...@"
]

It can be used at the end of an object to allow any extra keys:

{
  "id": 351,
  "name": "John Smith",
  "@...@": ""
}

Gherkin example

Gomatch was created to use it together with tools like GODOG. The goal was to be able to validate JSON response focusing only on parts essential in given scenario.

Feature: User management API
  In order to provide GUI for user management 
  As a frontent developer
  I need to be able to create, retrive, update and delete users

  Scenario: Get list of users sorted by username ascending
    Given the database contains users:
    | Username   | Email                  |
    | john.smith | john.smith@example.com |
    | alvin34    | alvin34@example.com    |
    | mike1990   | mike.jones@example.com |
    When I send "GET" request to "/v1/users?sortBy=username&sortDir=asc"
    Then the response code should be 200
    And the response body should match json:
    """
    {
      "items": [
        {
          "username": "alvin34",
          "@...@": ""
        },
        {
          "username": "john.smith",
          "@...@": ""
        },
        {
          "username": "mike1990",
          "@...@": ""
        }
      ],
      "@...@": ""
    }
    """

License

This library is distributed under the MIT license. Please see the LICENSE file.

Credits

This library was inspired by PHP Matcher

The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/). Gomatch logo was based on a gopher created by Takuya Ueda (https://twitter.com/tenntenn). Licensed under the Creative Commons 3.0 Attributions license. Gopher eyes were changed.

Documentation

Overview

Package gomatch provides types for pattern based JSON matching.

It provides JSONMatcher type which performs deep comparison of two JSON strings. JSONMatcher may be created with a set of ValueMatcher implementations. A ValueMatcher is used to make comparison less strict than a regular value comparison.

Use NewDefaultJSONMatcher to create JSONMatcher with a chain of all available ValueMatcher implementations.

Basic usage:

actual := `
{
	"id": 351,
	"name": "John Smith",
	"address": {
		"city": "Boston"
	}
}
`
expected := `
{
	"id": "@number@",
	"name": "John Smith",
	"address": {
		"city": "@string@"
	}
}
`

m := gomatch.NewDefaultJSONMatcher()
ok, err := m.Match(expected, actual)
if ok {
	fmt.Printf("actual JSON matches expected JSON")
} else {
	fmt.Printf("actual JSON does not match expected JSON: %s", err.Error())
}

Use NewJSONMatcher to create JSONMatcher with a custom ValueMatcher implementation. Use ChainMatcher to chain multiple ValueMacher implementations.

m := gomatch.NewJSONMatcher(
	NewChainMatcher(
		[]ValueMatcher{
			NewStringMatcher("@string@"),
			NewNumberMatcher("@number@"),
		},
	)
);

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ArrayMatcher

type ArrayMatcher struct {
	// contains filtered or unexported fields
}

An ArrayMatcher matches []interface{}.

func NewArrayMatcher

func NewArrayMatcher(pattern string) *ArrayMatcher

NewArrayMatcher creates ArrayMatcher.

func (*ArrayMatcher) CanMatch

func (m *ArrayMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*ArrayMatcher) Match

func (m *ArrayMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type BoolMatcher

type BoolMatcher struct {
	// contains filtered or unexported fields
}

A BoolMatcher matches booleans.

func NewBoolMatcher

func NewBoolMatcher(pattern string) *BoolMatcher

NewBoolMatcher creates BoolMatcher.

func (*BoolMatcher) CanMatch

func (m *BoolMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*BoolMatcher) Match

func (m *BoolMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type ChainMatcher

type ChainMatcher struct {
	// contains filtered or unexported fields
}

A ChainMatcher allows to chain multiple value matchers

func NewChainMatcher

func NewChainMatcher(matchers []ValueMatcher) *ChainMatcher

NewChainMatcher creates ChainMatcher.

func (*ChainMatcher) CanMatch

func (m *ChainMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled by any of internal matchers

func (*ChainMatcher) Match

func (m *ChainMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern. It iterates through internal matchers and uses first which can handle given pattern.

type EmailMatcher added in v1.1.0

type EmailMatcher struct {
	// contains filtered or unexported fields
}

An EmailMatcher matches email

func NewEmailMatcher added in v1.1.0

func NewEmailMatcher(pattern string) *EmailMatcher

NewEmailMatcher creates EmailMatcher.

func (*EmailMatcher) CanMatch added in v1.1.0

func (m *EmailMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*EmailMatcher) Match added in v1.1.0

func (m *EmailMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type JSONMatcher

type JSONMatcher struct {
	// contains filtered or unexported fields
}

A JSONMatcher provides Match method to match two JSONs with pattern matching support.

func NewDefaultJSONMatcher

func NewDefaultJSONMatcher() *JSONMatcher

NewDefaultJSONMatcher creates JSONMatcher with default chain of value matchers. Default chain contains:

- StringMatcher handling "@string@" pattern

- NumberMatcher handling "@number@" pattern

- BoolMatcher handling "@bool@" pattern

- ArrayMatcher handling "@array@" pattern

- UUIDMatcher handling "@uuid@" pattern

- EmailMatcher handling "@email@" pattern

- WildcardMatcher handling "@wildcard@" pattern

func NewJSONMatcher

func NewJSONMatcher(matcher ValueMatcher) *JSONMatcher

NewJSONMatcher creates JSONMatcher with given value matcher.

func (*JSONMatcher) Match

func (m *JSONMatcher) Match(expectedJSON, actualJSON string) (bool, error)

Match performs deep match of given JSON with an expected JSON pattern.

It traverses expected JSON pattern and checks if actual JSON has expected values. When traversing it checks if expected value is a pattern supported by internal ValueMatcher. In such case it uses the ValueMatcher to match actual value otherwise it compares expected value with actual value.

Expected JSON pattern example:

{
	"id": "@number@",
	"name": "John Smith",
	"address": {
		"city": "@string@"
	}
}

Matching actual JSON:

{
	"id": 351,
	"name": "John Smith",
	"address": {
		"city": "Boston"
	}
}

In above example we assume that ValueMatcher supports "@number@" and "@string@" patterns, otherwise matching will fail.

Besides value patterns JSONMatcher supports an "unbounded pattern" - "@...@". It can be used at the end of an array to allow any extra array elements:

[
	"John Smith",
	"Joe Doe",
	"@...@"
]

It can be used at the end of an object to allow any extra keys:

{
	"id": 351,
	"name": "John Smith",
	"@...@": ""
}

When matching fails then error message contains a path to invalid value.

type NumberMatcher

type NumberMatcher struct {
	// contains filtered or unexported fields
}

A NumberMatcher matches float64. It expects float64 because json.Unmarshal uses float64 by default for numbers.

func NewNumberMatcher

func NewNumberMatcher(pattern string) *NumberMatcher

NewNumberMatcher creates NumberMatcher.

func (*NumberMatcher) CanMatch

func (m *NumberMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*NumberMatcher) Match

func (m *NumberMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type StringMatcher

type StringMatcher struct {
	// contains filtered or unexported fields
}

A StringMatcher matches any string

func NewStringMatcher

func NewStringMatcher(pattern string) *StringMatcher

NewStringMatcher creates StringMatcher.

func (*StringMatcher) CanMatch

func (m *StringMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled.

func (*StringMatcher) Match

func (m *StringMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type UUIDMatcher

type UUIDMatcher struct {
	// contains filtered or unexported fields
}

A UUIDMatcher matches booleans.

func NewUUIDMatcher

func NewUUIDMatcher(pattern string) *UUIDMatcher

NewUUIDMatcher creates UUIDMatcher.

func (*UUIDMatcher) CanMatch

func (m *UUIDMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*UUIDMatcher) Match

func (m *UUIDMatcher) Match(p, v interface{}) (bool, error)

Match performs value matching against given pattern.

type ValueMatcher

type ValueMatcher interface {
	// CanMatch returns true if given pattern can be handled by value matcher implementation.
	CanMatch(p interface{}) bool

	// Match performs the matching of given value v.
	// It also expects pattern p so implementation may handle multiple patterns or some DSL.
	Match(p, v interface{}) (bool, error)
}

A ValueMatcher interface should be implemented by any matcher used by JSONMatcher.

type WildcardMatcher

type WildcardMatcher struct {
	// contains filtered or unexported fields
}

A WildcardMatcher matches any value.

func NewWildcardMatcher

func NewWildcardMatcher(pattern string) *WildcardMatcher

NewWildcardMatcher creates WildcardMatcher.

func (*WildcardMatcher) CanMatch

func (m *WildcardMatcher) CanMatch(p interface{}) bool

CanMatch returns true if pattern p can be handled

func (*WildcardMatcher) Match

func (m *WildcardMatcher) Match(p, v interface{}) (bool, error)

Match return true for any value

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL