Skip to content

Proposal: Pass custom formatters that are used in the failureMessage passed to assert/require.Error #1307

@jkopczyn

Description

@jkopczyn

The naive string representation of an object is not always a helpful descriptive label for it when it appears in a failed assertion or requirement, and trailing messages (usually msgAndArgs) are harder to parse visually than the main string block of an error message. Therefore, add a class of assertion methods which allow a wrapper to be applied.

Concrete example: I frequently work with protocol buffers which include status enums, and want to assert the expected status, e.g.

assert.Equal(t, pb.Status_VALID, resp.GetStatus())

When this fails, it gives this message:

Error:      	Not equal: 
            	expected: 2
            	actual  : 1
Test:       	TestCreationFooBar/create_asset

Protobuf enums have a .String() method to retrieve their names, so obviously, I can change the assert to

assert.Equal(t, pb.Status_VALID.String(), resp.GetStatus().String())

and that will make the message

Error:      	Not equal: 
            	expected: "VALID"
            	actual  : "IN_PROGRESS"
Test:       	TestCreationFooBar/create_asset

This is much easier to read, and to distinguish from a slice with the wrong length or whatever, but it's a more brittle comparison - the integer comparison is the canonical one, it's just not human-legible.

The obvious way to get around this problem is to have variant types assert.EqualWithWrapper, assert.ElementsMatchWithWrapper, etc. which accept a func(x interface{})string which is applied to the elements being compared before being combined into the error message. The one I'd use here, which would probably be fairly typical, might be func(x Stringable) { return fmt.Sprintf("%v [%s]", x, x.String()) }.

I'm not sure how many methods of the API would benefit from having this equivalent. Equal, EqualValues, Exactly, and NotEqual seem obvious; ElementsMatch, Contains/NotContains, and Subset/NotSubset also seem like they'd benefit. Many others might but it'd be niche, like WithinRange; others like Implements would require a more-complicated type signature (and would still probably be niche). There's clearly a steep tradeoff of API verbosity for this functionality, which isn't ideal, but I think it's worth doing at least in some cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    pkg-assertChange related to package testify/assertquestionQuestions related to API usagewontfix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions