google/go-cmp

How to implement jests.js's `expect.any(String)` like functionality?

safareli opened this issue · 1 comments

I'm comparing 2 maps one is from http response and one expected response. As response contains id which is random I want to ignore that property, of course I can delete that property from response and do comparison that way but there are other similar usecases, what if we have id's deeper in an object. deleting things is cumbersome .
I want to basically implement somehow some value anyString such that this will "pass":

diff(
  map[string]any{
    "fname":  "jo",
    "lname":  "do,
    "id": "asd-ad12-3rfs-ad4"
  },
  map[string]any{
    "fname":  "jo",
    "lname":  "do,
    "id": anyString
  },
)

Jest.js has something similar expect.any(constructor)

expect(val).toEqual({
  data: expect.any(Array),
  active: true,
  ID: expect.any(String),
})

This expect.any(constructor) returns special value that is used during diffing to only check constructors of values.

I tried to implement anyString:

type AnyString struct {
	value   string
	isInput bool
}

var anyString = AnyString{}

func TestAnyString(t *testing.T) {
	// Example usage
	x := map[string]any{
		"foo": "12",
	}
	y := map[string]any{
		"foo": anyString,
	}

	optTrans := cmp.Transformer("strinany", func(input string) AnyString {
		return AnyString{value: input, isInput: true}
	})
	optComp := cmp.Comparer(func(x, y AnyString) bool {
		// if one is input string and one is anyString we match
		if x.isInput == !y.isInput {
			return true
		}

		return x == y
	})

	if diff := cmp.Diff(x, y, optTrans, optComp); diff != "" {
		fmt.Printf("Mismatch (-x +y):\n%s", diff)
		t.Fail()
	} else {
		fmt.Println("Match")
	}
}

but I am getting:

  map[string]any{
- 	"foo": string("12"),
+ 	"foo": testz.AnyString{},
  }

as I understand the transformation only work if both values on each side are of same type.

Is there any way I can implement that with this library?

Related issues

#270
#261

Well actually thanks to example https://play.golang.org/p/iRvh9tTJk_q shared by @dsnet here #261 (comment) I was able to do what I wanted.

type AnyString struct {
	value   string
	isInput bool
}

var anyString = AnyString{}

func TestAnyString(t *testing.T) {
	// Example usage
	x := map[string]any{
		"foo": "12",
	}
	y := map[string]any{
		"foo": anyString,
	}

	tr := cmp.FilterValues(func(x, y interface{}) bool {
		_, okX1 := x.(string)
		_, okX2 := x.(AnyString)
		_, okY1 := y.(string)
		_, okY2 := y.(AnyString)
		return (okX1 || okX2) && (okY1 || okY2)
	}, cmp.Transformer("AnyStringTransformer", func(x interface{}) AnyString {
		if is, ok := x.(string); ok {
			return AnyString{value: is, isInput: true}
		}
		return x.(AnyString)
	}))

	optComp := cmp.Comparer(func(x AnyString, y AnyString) bool {
		// if one is input string and one is anyString we match
		if x.isInput == !y.isInput {
			return true
		}

		return x == y
	})

	if diff := cmp.Diff(x, y, tr, optComp); diff != "" {
		fmt.Printf("Mismatch (-x +y):\n%s", diff)
		t.Fail()
	} else {
		fmt.Println("Match")
	}
}