stretchr / testify

A toolkit with common assertions and mocks that plays nicely with the standard library
MIT License
22.5k stars 1.56k forks source link

call.Unset() panics when call := mockObj.On("Foo", mock.AnythingOfType("foo")) #1599

Open jeandeducla opened 1 month ago

jeandeducla commented 1 month ago

Description

When you use mock.AnythingOfType("foo") in On(...) and then try to call Unset() on the resulting call, the code panics.

Step To Reproduce

Here is a simple snippet that reproduces the issue:

import (
    "testing"

    "github.com/stretchr/testify/mock"
)

type Foo struct {
    doer Doer
}

type Doer interface {
    DoSomething(number int) (bool, error)
}

func (f Foo) Do(number int) (bool, error) {
    return f.doer.DoSomething(number)
}

type MyMockedObject struct {
    mock.Mock
}

func (m *MyMockedObject) DoSomething(number int) (bool, error) {
    args := m.Called(number)
    return args.Bool(0), args.Error(1)
}

func TestSomething(t *testing.T) {
    testObj := new(MyMockedObject)

    foo := Foo{doer: testObj}

    call := testObj.On("DoSomething", 2).Return(true, nil)
    _, _ = foo.Do(2)
    testObj.AssertExpectations(t)
    call.Unset() // Works!

    call = testObj.On("DoSomething", mock.AnythingOfType("int")).Return(true, nil)
    _, _ = foo.Do(2)
    testObj.AssertExpectations(t)
    call.Unset() // panics...
}

Expected behavior

I expect to be able to call Unset() on a call that has been mocked with mock.AnythingOfType (or did I miss something on how to use the API? Happy to get feedback here :-))

Actual behavior

When calling Unset() on a call that has been mocked with mock.AnythingOfType, it panics.

jeandeducla commented 1 month ago

from locally debugging, I have the impression the call to Diff in Unset is not correctly comparing the arguments here; it does not cover the case of actual being a anythingOfTypeArgument. Locally I did that:

              switch expected := expected.(type) {                                                                                                 
              case anythingOfTypeArgument:
                  // type checking
~                 switch actual := actual.(type) {
~                 case anythingOfTypeArgument:     
~                     if string(actual) != string(expected) {     
~                         differences++     
+                     }     
+                 default:     
+                     if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {     
+                         // not match     
+                         differences++     
+                         output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actu
+                     }     
                  }                                                                        

which does the trick.

Happy to make a PR