Skip to content

Commit

Permalink
Introduce non-global fail handler
Browse files Browse the repository at this point in the history
  • Loading branch information
petergtz committed Jan 17, 2019
1 parent b113d17 commit 6d417a0
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 17 deletions.
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,36 @@ Getting Started
Using Pegomock with Golang’s XUnit-style Tests
----------------------------------------------

Use it like this:
The preferred way is:

```go

import (
"github.com/petergtz/pegomock"
"testing"
)

func TestUsingMocks(t *testing.T) {
pegomock.RegisterMockTestingT(t)
mock := NewMockPhoneBook(pegomock.WithT(t))

// use Pegomock here
// use your mock here
}
```

There are two caveats:

- You must register the `t *testing.T` passed to your test with Pegomock before you make any verifications associated with that test. So every `Test...` function in your suite should have the `RegisterTestingT(t)` line.
- Pegomock uses a global (singleton) fail handler. This has the benefit that you don’t need to pass the fail handler down to each test, but does mean that you cannot run your XUnit style tests in parallel with Pegomock.
Alternatively, you can set a global fail handler within your test:

```go
func TestUsingMocks(t *testing.T) {
pegomock.RegisterMockTestingT(t)

mock := NewMockPhoneBook()

// use your mock here
}
```
**Note:** In this case, Pegomock uses a global (singleton) fail handler. This has the benefit that you don’t need to pass the fail handler down to each test, but does mean that you cannot run your XUnit style tests in parallel with Pegomock.

If you configure both a global fail handler and a specific one for your mock, the specific one overrides the global fail handler.

Using Pegomock with Ginkgo
--------------------------
Expand Down
20 changes: 14 additions & 6 deletions dsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func RegisterMockFailHandler(handler FailHandler) {
GlobalFailHandler = handler
}
func RegisterMockTestingT(t *testing.T) {
RegisterMockFailHandler(BuildTestingTGomegaFailHandler(t))
RegisterMockFailHandler(BuildTestingTFailHandler(t))
}

var (
Expand All @@ -57,6 +57,7 @@ type invocation struct {
type GenericMock struct {
sync.Mutex
mockedMethods map[string]*mockedMethod
fail FailHandler
}

func (genericMock *GenericMock) Invoke(methodName string, params []Param, returnTypes []reflect.Type) ReturnValues {
Expand Down Expand Up @@ -103,8 +104,12 @@ func (genericMock *GenericMock) Verify(
if len(options) == 1 {
timeout = options[0].(time.Duration)
}
if GlobalFailHandler == nil {
panic("No GlobalFailHandler set. Please use either RegisterMockFailHandler or RegisterMockTestingT to set a fail handler.")
if genericMock.fail == nil && GlobalFailHandler == nil {
panic("No FailHandler set. Please use either RegisterMockFailHandler or RegisterMockTestingT or TODO to set a fail handler.")
}
fail := GlobalFailHandler
if genericMock.fail != nil {
fail = genericMock.fail
}
defer func() { globalArgMatchers = nil }() // We don't want a panic somewhere during verification screw our global argMatchers

Expand All @@ -124,7 +129,7 @@ func (genericMock *GenericMock) Verify(
// if time.Since(startTime) < timeout {
// continue timeoutLoop
// }
GlobalFailHandler(fmt.Sprintf("Expected function call %v(%v) before function call %v(%v)",
fail(fmt.Sprintf("Expected function call %v(%v) before function call %v(%v)",
methodName, formatParams(params), inOrderContext.lastInvokedMethodName, formatParams(inOrderContext.lastInvokedMethodParams)))
}
inOrderContext.invocationCounter = methodInvocation.orderingInvocationNumber
Expand All @@ -145,7 +150,7 @@ func (genericMock *GenericMock) Verify(
if timeout > 0 {
timeoutInfo = fmt.Sprintf(" after timeout of %v", timeout)
}
GlobalFailHandler(fmt.Sprintf(
fail(fmt.Sprintf(
"Mock invocation count for %v(%v) does not match expectation%v.\n\n\t%v\n\n\t%v",
methodName, paramsOrMatchers, timeoutInfo, invocationCountMatcher.FailureMessage(), formatInteractions(genericMock.allInteractions())))
}
Expand Down Expand Up @@ -468,7 +473,10 @@ func GetGenericMockFrom(mock Mock) *GenericMock {
genericMocksMutex.Lock()
defer genericMocksMutex.Unlock()
if genericMocks[mock] == nil {
genericMocks[mock] = &GenericMock{mockedMethods: make(map[string]*mockedMethod)}
genericMocks[mock] = &GenericMock{
mockedMethods: make(map[string]*mockedMethod),
fail: mock.FailHandler(),
}
}
return genericMocks[mock]
}
Expand Down
14 changes: 14 additions & 0 deletions dsl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,20 @@ var _ = Describe("MockDisplay", func() {
Expect(input[0].i).To(Equal(3))
})
})

Context("Mock created with custom fail handler", func() {
It("calls custom fail handler instead of global one", func() {
failHandlerCalled := false
display := NewMockDisplay(WithFailHandler(func(message string, callerSkip ...int) {
failHandlerCalled = true
}))

display.VerifyWasCalledOnce().Show("This was never called")

Expect(failHandlerCalled).To(BeTrue())
})
})

})

func flattenStringSliceOfSlices(sliceOfSlices [][]string) (result []string) {
Expand Down
11 changes: 9 additions & 2 deletions mockgen/mockgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,16 @@ func (g *generator) generateMockType(mockTypeName string) {
p(" fail func(message string, callerSkip ...int)").
p("}").
emptyLine().
p("func New%v() *%v {", mockTypeName, mockTypeName).
p(" return &%v{fail: pegomock.GlobalFailHandler}", mockTypeName).
p("func New%v(options ...pegomock.Option) *%v {", mockTypeName, mockTypeName).
p(" mock := &%v{}", mockTypeName).
p(" for _, option := range options {").
p(" option.Apply(mock)").
p(" }").
p(" return mock").
p("}").
emptyLine().
p("func (mock *%v) SetFailHandler(fh pegomock.FailHandler) { mock.fail = fh }", mockTypeName).
p("func (mock *%v) FailHandler() pegomock.FailHandler { return mock.fail }", mockTypeName).
emptyLine()
}

Expand Down
6 changes: 5 additions & 1 deletion testing_t_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type testingT interface {
Errorf(format string, args ...interface{})
}

func BuildTestingTGomegaFailHandler(t testingT) FailHandler {
func BuildTestingTFailHandler(t testingT) FailHandler {
return func(message string, callerSkip ...int) {
skip := 1
if len(callerSkip) > 0 {
Expand All @@ -57,3 +57,7 @@ func pruneStack(fullStackTrace string, skip int) string {
}
return strings.Join(prunedStack, "\n")
}

func WithT(t testingT) Option {
return WithFailHandler(BuildTestingTFailHandler(t))
}
15 changes: 14 additions & 1 deletion types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,20 @@ package pegomock

type FailHandler func(message string, callerSkip ...int)

type Mock interface{}
type Mock interface {
SetFailHandler(FailHandler)
FailHandler() FailHandler
}
type Param interface{}
type ReturnValue interface{}
type ReturnValues []ReturnValue

type Option interface{ Apply(Mock) }

type OptionFunc func(mock Mock)

func (f OptionFunc) Apply(mock Mock) { f(mock) }

func WithFailHandler(fail FailHandler) Option {
return OptionFunc(func(mock Mock) { mock.SetFailHandler(fail) })
}

0 comments on commit 6d417a0

Please sign in to comment.