Add T object and Test method to Mock

This makes is possible to fail the test instead of panicing in case
that the method was called with unexpected arguments.

Fixes #489

mock: change field 'T' to be private field 'test'
mock_test: MockTestingT not using Mock anymore
pull/581/head
Eyal Posener 2018-03-06 07:29:23 +02:00 committed by Ernesto Jiménez
parent 20dae58180
commit 33951ec724
2 changed files with 85 additions and 5 deletions

View File

@ -191,6 +191,10 @@ type Mock struct {
// Holds the calls that were made to this mocked object.
Calls []Call
// test is An optional variable that holds the test struct, to be used when an
// invalid mock call was made.
test TestingT
// TestData holds any data that might be useful for testing. Testify ignores
// this data completely allowing you to do whatever you like with it.
testData objx.Map
@ -213,6 +217,27 @@ func (m *Mock) TestData() objx.Map {
Setting expectations
*/
// Test sets the test struct variable of the mock object
func (m *Mock) Test(t TestingT) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.test = t
}
// fail fails the current test with the given formatted format and args.
// In case that a test was defined, it uses the test APIs for failing a test,
// otherwise it uses panic.
func (m *Mock) fail(format string, args ...interface{}) {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.test == nil {
panic(fmt.Sprintf(format, args...))
}
m.test.Errorf(format, args...)
m.test.FailNow()
}
// On starts a description of an expectation of the specified method
// being called.
//
@ -329,14 +354,14 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
m.mutex.Unlock()
if closestCall != nil {
panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s",
m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s",
callString(methodName, arguments, true),
callString(methodName, closestCall.Arguments, true),
diffArguments(closestCall.Arguments, arguments),
strings.TrimSpace(mismatch)),
strings.TrimSpace(mismatch),
)
} else {
panic(fmt.Sprintf("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()))
m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo())
}
}

View File

@ -4,12 +4,11 @@ import (
"errors"
"fmt"
"regexp"
"runtime"
"sync"
"testing"
"time"
"runtime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -93,6 +92,34 @@ func (i *TestExampleImplementation) TheExampleMethodFuncType(fn ExampleFuncType)
return args.Error(0)
}
// MockTestingT mocks a test struct
type MockTestingT struct {
logfCount, errorfCount, failNowCount int
}
const mockTestingTFailNowCalled = "FailNow was called"
func (m *MockTestingT) Logf(string, ...interface{}) {
m.logfCount++
}
func (m *MockTestingT) Errorf(string, ...interface{}) {
m.errorfCount++
}
// FailNow mocks the FailNow call.
// It panics in order to mimic the FailNow behavior in the sense that
// the execution stops.
// When expecting this method, the call that invokes it should use the following code:
//
// assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {...})
func (m *MockTestingT) FailNow() {
m.failNowCount++
// this function should panic now to stop the execution as expected
panic(mockTestingTFailNowCalled)
}
/*
Mock
*/
@ -205,6 +232,34 @@ func Test_Mock_On_WithIntArgMatcher(t *testing.T) {
})
}
func TestMock_WithTest(t *testing.T) {
var (
mockedService TestExampleImplementation
mockedTest MockTestingT
)
mockedService.Test(&mockedTest)
mockedService.On("TheExampleMethod", 1, 2, 3).Return(0, nil)
// Test that on an expected call, the test was not failed
mockedService.TheExampleMethod(1, 2, 3)
// Assert that Errorf and FailNow were not called
assert.Equal(t, 0, mockedTest.errorfCount)
assert.Equal(t, 0, mockedTest.failNowCount)
// Test that on unexpected call, the mocked test was called to fail the test
assert.PanicsWithValue(t, mockTestingTFailNowCalled, func() {
mockedService.TheExampleMethod(1, 1, 1)
})
// Assert that Errorf and FailNow were called once
assert.Equal(t, 1, mockedTest.errorfCount)
assert.Equal(t, 1, mockedTest.failNowCount)
}
func Test_Mock_On_WithPtrArgMatcher(t *testing.T) {
var mockedService TestExampleImplementation