0

I use Testify to create a unit test for my golang app. I need to create a unit test for this function where it calls a variadic function (function with trailing arguments). I encountered an error when I test it. I'm actually not sure if the error is because of the trailing argument itself or not, but I feel like there's something wrong with the mock.

// svc/callThisFunction.go
// data type of args is []sqkit.SelectOption
func CallThisFunction(ctx context.Context, args ...sqkit.SelectFunctiom) (result string, err error) {
  return result, nil
}

// svc/functionToTest.go
// This is the function that I wanna test
func FunctionToTest(ctx context.Context, id int64) (result string, err error) {
  args := []sqkit.SelectOption{
    sqkit.Where{
      fmt.Sprintf("id = %d", id),
    },
  }

  newResult, err := callThisFunctionService.CallThisFunction(ctx, args)
  if err != nil {
    return newResult, err
  }

  return newResult, nil
}
// svc/functionToTest_test.go

func Test_FunctionToTest(t *testing.T) {
  testCase := []struct {
    name string
    id int64
    onCallThisFunctionMock func(callThisFunctionSvc *mocks.CallThisFunctionSvc)
    expectedResult string
    wantError bool
    expectedError error  
  }{
      {
        name: "Success",
        id: 1,
        onCallThisFunctionMock: func(callThisFunctionSvc *mocks.CallThisFunctionSvc) {
          // NOTE: I've created 2 different versions (used separately, not at the same), using mock.Anything() and using actual arguments
          // Both of these give the same errors
          // Using actual arguments          
          args := []sqkit.SelectOption{
            sqkit.Where{
              fmt.Sprintf("id = %d", 1},
            },
          }
          callThisFunctionSvc.On("CallThisFunction", context.Background(), args).Return("Success", nil)

          // Using mock.Anything
          callThisFunctionSvc.On("CallThisFunction", context.Background(), mock.Anything).Return("Success", nil)
        }
      }
   }

   for _, tc := range testCases {
     var callThisFunctionSvc = new(mocks.CallThisFunctionSvc)

     tc.onCallThisFunctionMock(callThisFunctionSvc)

     svc := &svc.FunctionToTest{
       CallThisFunction: callThisFunctionSvc,
     }

     actualResult, actualError := svc.FunctionToTest(context.Background(), tc.id)

     if tc.wantEror {
       require.Error(t, actualError, tc.expectedError)
     } else {
       require.NoError(t, actualError)
     }

     require.Equal(t, tc.expectedResult, actualResult)
   }
}

This is the error it gives

=== RUN   Test_GenerateDocument
--- FAIL: Test_GenerateDocument (0.00s)
panic: 
assert: mock: I don't know what to return because the method call was unexpected.
        Either do Mock.On("CallThisFunction").Return(...) first, or remove the GetTemplates() call.
        This method was unexpected:
                CallThisFunction(*context.emptyCtx,sqkit.Where)
                0: (*context.emptyCtx)(0xc0000a4010)
                1: sqkit.Where{"id = 1"}

Usually, when I encountered an error like this, it's because I haven't defined the return values of the function calls inside the function I wanna test. But this time I've created it, but it somehow can't read the return. Any idea why?

new line
  • 183
  • 2
  • 9

1 Answers1

0

The error indicates you called CallThisFuncion with params (context.Context, sqkit.Where), but your example is using and setting the expectation for (context.Context, []sqkit.Option). The example with mock.Anything should work, but I believe it's failing because of the context. You'll need to set the expectation with the same context you're passing down. If FunctionToTest is going to be altering the context, I believe you'll need to use mock.Anything instead.

func Test_FunctionToTest(t *testing.T) {
  testCase := []struct {
    name string
    id int64
    onCallThisFunctionMock func(context.Context, *mocks.CallThisFunctionSvc)
    expectedResult string
    wantError bool
    expectedError error  
  }{
      {
        name: "Success",
        id: 1,
        onCallThisFunctionMock: func(ctx context.Context, callThisFunctionSvc *mocks.CallThisFunctionSvc) {
          args := []sqkit.SelectOption{
            sqkit.Where{
              fmt.Sprintf("id = %d", 1},
            },
          }
          callThisFunctionSvc.On("CallThisFunction", ctx, args).Return("Success", nil)
        }
      }
  }

  for _, tc := range testCases {
    var callThisFunctionSvc = new(mocks.CallThisFunctionSvc)
    var ctx = context.Background()

    tc.onCallThisFunctionMock(ctx, callThisFunctionSvc)

    svc := &svc.FunctionToTest{
      CallThisFunction: callThisFunctionSvc,
    }

    actualResult, actualError := svc.FunctionToTest(ctx, tc.id)

    if tc.wantEror {
      require.Error(t, actualError, tc.expectedError)
    } else {
      require.NoError(t, actualError)
    }

    require.Equal(t, tc.expectedResult, actualResult)
  }
}

If you want to ensure a context.Context was passed as the first parameter but don't care what context, you could use AnythingOfType.

callThisFunctionSvc.On("CallThisFunction", mock.AnythingOfType("context.Context"), args).Return("Success", nil)
Gavin
  • 4,365
  • 1
  • 18
  • 27