Readme.md
1Structural equality library for Golang.
2
3## Story
4
5While we were working on [HappyPancake](http://abdullin.com/happypancake/) project, [Pieter](https://twitter.com/pjvds) always wanted to have a better assertion library for our event-driven specifications.
6
7Months later, awesome folks from [@DDDBE](https://twitter.com/dddbe) community presented me with some Trappist Beer. Thanks to it (and some spare time on the weekend), this assertion library was finally written.
8
9## How it works
10
11You 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`.
12
13Maps 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.
14
15Consider following types:
16
17```go
18type Robot struct {
19 Legs int `json:"legs"`
20 Arms int `json:"arms"`
21 Name string `json:"name"`
22}
23
24type Party struct {
25 Rating []int `json:"rating"`
26 Seating map[string]*Robot `json:"seating"`
27}
28```
29Let's imagine that our JSON API returns `Party` object, which we want to verify. We could define our expectation like this:
30
31```go
32 expect := seq.Map{
33 // array
34 "rating.len": 3,
35 "rating[1]": 5,
36 // flat path with value terminator
37 "seating.front.name": "R2D2",
38 "seating.front.arms": "1",
39 "seating.front.legs": 3,
40 // flat path with map terminator
41 "seating.right": seq.Map{
42 "name": "C3PO",
43 },
44 // flat path with object terminator
45 "seating.back": &Robot{
46 Name: "Marvin",
47 Legs: 2,
48 Arms: 2,
49 },
50 }
51
52```
53Once you have the expectation, you could compare it with an actual object. Here is an example of a valid object:
54```go
55 actual := &Party{
56 Rating: []int{4, 5, 4},
57 Seating: map[string]*Robot{
58 "front": &Robot{
59 Name: "R2D2",
60 Arms: 1,
61 Legs: 3,
62 },
63 "back": &Robot{
64 Name: "Marvin",
65 Legs: 2,
66 Arms: 2,
67 },
68 "right": &Robot{
69 Name: "C3PO",
70 Legs: 2,
71 Arms: 2,
72 },
73 },
74 }
75 result := expect.Test(actual)
76
77```
78Result value would contain `Issues []seq.Issue` with any differences and could be checked like this:
79
80```go
81if !result.Ok() {
82 fmt.Println("Differences")
83 for _, v := range result.Issues {
84 fmt.Println(v.String())
85 }
86}
87```
88
89If actual object has some invalid or missing properties, then result will have nice error messages. Consider this object:
90
91```go
92 actual := &Party{
93 Seating: map[string]*Robot{
94 "front": &Robot{
95 Name: "R2D2",
96 Arms: 1,
97 },
98 "back": &Robot{
99 Name: "Marvin",
100 Arms: 3,
101 },
102 "right": &Robot{
103 Name: "C4PO",
104 Legs: 2,
105 Arms: 3,
106 },
107 },
108 }
109
110```
111
112If verified against the original expectation, `result.Issues` would contain these error messages:
113
114```
115Expected rating.len to be '3' but got nothing
116Expected seating.back.legs to be '2' but got '0'
117Expected seating.front.legs to be '3' but got '0'
118Expected rating[1] to be '5' but got nothing
119Expected seating.back.arms to be '2' but got '3'
120Expected seating.right.name to be 'C3PO' but got 'C4PO'
121```
122Check out the [unit tests](https://github.com/abdullin/seq/blob/master/seq_test.go) for more examples.
123
124## Feedback
125
126Feedback is welcome and appreciated!
127