The Central Go Modules Repository

To use GoCenter:
export GOPROXY=https://gocenter.io
7
Stars
BSD-2-Clause
License
17,833
Downloads
January 22nd 2018
Last Modified
Version:
Loading...

Structural equality library for Golang.

Story

While we were working on HappyPancake project, Pieter always wanted to have a better assertion library for our event-driven specifications.

Months later, awesome folks from @DDDBE community presented me with some Trappist Beer. Thanks to it (and some spare time on the weekend), this assertion library was finally written.

How it works

You can define expectations on objects (e.g. API responses or expected events) by creating an instance of seq.Map, which is provided by the package github.com/abdullin/seq.

Maps can be nested or they could have flat paths. Values could be represented with strings, primitive types, instances of seq.Map or JSON-serializable objects.

Consider following types:

type Robot struct {
    Legs int    `json:"legs"`
    Arms int    `json:"arms"`
    Name string `json:"name"`
}

type Party struct {
    Rating  []int             `json:"rating"`
    Seating map[string]*Robot `json:"seating"`
}

Let’s imagine that our JSON API returns Party object, which we want to verify. We could define our expectation like this:

  expect := seq.Map{
    // array
    "rating.len": 3,
    "rating[1]":  5,
    // flat path with value terminator
    "seating.front.name": "R2D2",
    "seating.front.arms": "1",
    "seating.front.legs": 3,
    // flat path with map terminator
    "seating.right": seq.Map{
      "name": "C3PO",
    },
    // flat path with object terminator
    "seating.back": &Robot{
      Name: "Marvin",
      Legs: 2,
      Arms: 2,
    },
  }

Once you have the expectation, you could compare it with an actual object. Here is an example of a valid object:

  actual := &Party{
    Rating: []int{4, 5, 4},
    Seating: map[string]*Robot{
      "front": &Robot{
        Name: "R2D2",
        Arms: 1,
        Legs: 3,
      },
      "back": &Robot{
        Name: "Marvin",
        Legs: 2,
        Arms: 2,
      },
      "right": &Robot{
        Name: "C3PO",
        Legs: 2,
        Arms: 2,
      },
    },
  }
  result := expect.Test(actual)

Result value would contain Issues []seq.Issue with any differences and could be checked like this:

if !result.Ok() {
  fmt.Println("Differences")
  for _, v := range result.Issues {
    fmt.Println(v.String())
  }
}

If actual object has some invalid or missing properties, then result will have nice error messages. Consider this object:

  actual := &Party{
    Seating: map[string]*Robot{
      "front": &Robot{
        Name: "R2D2",
        Arms: 1,
      },
      "back": &Robot{
        Name: "Marvin",
        Arms: 3,
      },
      "right": &Robot{
        Name: "C4PO",
        Legs: 2,
        Arms: 3,
      },
    },
  }

If verified against the original expectation, result.Issues would contain these error messages:

Expected rating.len to be '3' but got nothing
Expected seating.back.legs to be '2' but got '0'
Expected seating.front.legs to be '3' but got '0'
Expected rating[1] to be '5' but got nothing
Expected seating.back.arms to be '2' but got '3'
Expected seating.right.name to be 'C3PO' but got 'C4PO'

Check out the unit tests for more examples.

Feedback

Feedback is welcome and appreciated!