Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Improve default failed equality match output to more clearly show diffs #616

@adamvictorclever

Description

@adamvictorclever

Requested feature
Sometimes I'll generate a mock and EXPECT some calls. In cases where the test fails because the mock was called with a different input, it can be very hard to tell what the difference is. This is especially apparent in cases where some fields are pointers.

I've tried to set up an example in Go Playground to show what I mean: https://go.dev/play/p/pVxqpZpqiwz

Basically, I have a struct that looks like

type Obo struct {
	a *string
	b *bool
	c []*Obo
}

and an interface I generate a mock for that looks like

type TestThing interface {
	DoThing(obo Obo)
}

In my TestWithMock case, I expect a call with a certain Obo and provide a call with a slightly different one, causing a test failure. The only difference between the two provided is that the first one has an a value set to &"apple" and the second set to &"banana".

Here's the output I see when the test fails:

=== RUN   TestWithMock
    prog.go:25: Unexpected call to *main.MockTestThing.DoThing([{0xc0000a6f50 0xc0000c2998 [0xc0000a9440]}]) at /tmp/sandbox1909100993/prog.go:25 because: 
        expected call at /tmp/sandbox1909100993/prog.go:24 doesn't match the argument at index 0.
        Got: {0xc0000a6f50 0xc0000c2998 [0xc0000a9440]} (main.Obo)
        Want: is equal to {0xc0000a6ef0 0xc0000c2988 [0xc0000a93b0]} (main.Obo)
    controller.go:137: missing call(s) to *main.MockTestThing.DoThing(is equal to {0xc0000a6ef0 0xc0000c2988 [0xc0000a93b0]} (main.Obo)) /tmp/sandbox1909100993/prog.go:24
    controller.go:137: aborting test due to missing call(s)
--- FAIL: TestWithMock (0.00s)

In reality, the diff is quite small, just a single field with a different value. However, the output from the failure provides basically no information on this mismatch. Every field appears different because the only thing that it displays is the pointer, not the value. This makes the issue much more difficult to debug!

Compare this to the output of TestWithAssert, a similar case that directly compares the two Obo objects using assert.Equal from the github.com/stretchr/testify/assert package.

=== RUN   TestWithAssert
    prog.go:29: 
        	Error Trace:	prog.go:29
        	Error:      	Not equal: 
        	            	expected: main.Obo{a:(*string)(0xc000184000), b:(*bool)(0xc000180008), c:[]*main.Obo{(*main.Obo)(0xc000188000)}}
        	            	actual  : main.Obo{a:(*string)(0xc000184020), b:(*bool)(0xc000180009), c:[]*main.Obo{(*main.Obo)(0xc000188030)}}
        	            	
        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1,3 +1,3 @@
        	            	 (main.Obo) {
        	            	- a: (*string)((len=5) "apple"),
        	            	+ a: (*string)((len=6) "banana"),
        	            	  b: (*bool)(true),
        	Test:       	TestWithAssert
--- FAIL: TestWithAssert (0.00s)

With this output, the nice diff immediately makes it clear what the difference was between the two structs, vastly simplifying my debugging experience!

I believe this is related to #265, which was closed. If I understand correctly, the proposed solution would be to use WantFormatter and GotFormatter to generate a nicer output myself. Personally, having never used these features before, it feels like a lot of effort for a simple and common problem! Would I need to use these in every test case I have? I think it would be much more helpful to improve the default output when using an equality matcher.

(Optional) Proposed solution
Some potential ideas for how I'd like to see the output improved

  • When outputting a failure, show the value for pointer fields. For example, something vaguely like
Got: {0xc0000a6f50("apple") ...} (main.Obo)
Want: is equal to {0xc0000a6ef0("banana") ...} (main.Obo)

This would make it easier to manually inspect and see what the difference is

  • Output a diff, like the one included by the assert function in my example above. This is even better since it makes it really easy to tell the difference.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions