123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // Copyright 2010 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // GoMock - a mock framework for Go.
- //
- // Standard usage:
- // (1) Define an interface that you wish to mock.
- // type MyInterface interface {
- // SomeMethod(x int64, y string)
- // }
- // (2) Use mockgen to generate a mock from the interface.
- // (3) Use the mock in a test:
- // func TestMyThing(t *testing.T) {
- // mockCtrl := gomock.NewController(t)
- // defer mockCtrl.Finish()
- //
- // mockObj := something.NewMockMyInterface(mockCtrl)
- // mockObj.EXPECT().SomeMethod(4, "blah")
- // // pass mockObj to a real object and play with it.
- // }
- //
- // By default, expected calls are not enforced to run in any particular order.
- // Call order dependency can be enforced by use of InOrder and/or Call.After.
- // Call.After can create more varied call order dependencies, but InOrder is
- // often more convenient.
- //
- // The following examples create equivalent call order dependencies.
- //
- // Example of using Call.After to chain expected call order:
- //
- // firstCall := mockObj.EXPECT().SomeMethod(1, "first")
- // secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
- // mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
- //
- // Example of using InOrder to declare expected call order:
- //
- // gomock.InOrder(
- // mockObj.EXPECT().SomeMethod(1, "first"),
- // mockObj.EXPECT().SomeMethod(2, "second"),
- // mockObj.EXPECT().SomeMethod(3, "third"),
- // )
- //
- // TODO:
- // - Handle different argument/return types (e.g. ..., chan, map, interface).
- package gomock
- import (
- "fmt"
- "reflect"
- "sync"
- )
- // A TestReporter is something that can be used to report test failures.
- // It is satisfied by the standard library's *testing.T.
- type TestReporter interface {
- Errorf(format string, args ...interface{})
- Fatalf(format string, args ...interface{})
- }
- // A Controller represents the top-level control of a mock ecosystem.
- // It defines the scope and lifetime of mock objects, as well as their expectations.
- // It is safe to call Controller's methods from multiple goroutines.
- type Controller struct {
- mu sync.Mutex
- t TestReporter
- expectedCalls callSet
- }
- func NewController(t TestReporter) *Controller {
- return &Controller{
- t: t,
- expectedCalls: make(callSet),
- }
- }
- func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
- recv := reflect.ValueOf(receiver)
- for i := 0; i < recv.Type().NumMethod(); i++ {
- if recv.Type().Method(i).Name == method {
- return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
- }
- }
- ctrl.t.Fatalf("gomock: failed finding method %s on %T", method, receiver)
- // In case t.Fatalf does not panic.
- panic(fmt.Sprintf("gomock: failed finding method %s on %T", method, receiver))
- }
- func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
- // TODO: check arity, types.
- margs := make([]Matcher, len(args))
- for i, arg := range args {
- if m, ok := arg.(Matcher); ok {
- margs[i] = m
- } else if arg == nil {
- // Handle nil specially so that passing a nil interface value
- // will match the typed nils of concrete args.
- margs[i] = Nil()
- } else {
- margs[i] = Eq(arg)
- }
- }
- ctrl.mu.Lock()
- defer ctrl.mu.Unlock()
- call := &Call{t: ctrl.t, receiver: receiver, method: method, methodType: methodType, args: margs, minCalls: 1, maxCalls: 1}
- ctrl.expectedCalls.Add(call)
- return call
- }
- func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
- ctrl.mu.Lock()
- defer ctrl.mu.Unlock()
- expected := ctrl.expectedCalls.FindMatch(receiver, method, args)
- if expected == nil {
- ctrl.t.Fatalf("no matching expected call: %T.%v(%v)", receiver, method, args)
- }
- // Two things happen here:
- // * the matching call no longer needs to check prerequite calls,
- // * and the prerequite calls are no longer expected, so remove them.
- preReqCalls := expected.dropPrereqs()
- for _, preReqCall := range preReqCalls {
- ctrl.expectedCalls.Remove(preReqCall)
- }
- rets, action := expected.call(args)
- if expected.exhausted() {
- ctrl.expectedCalls.Remove(expected)
- }
- // Don't hold the lock while doing the call's action (if any)
- // so that actions may execute concurrently.
- // We use the deferred Unlock to capture any panics that happen above;
- // here we add a deferred Lock to balance it.
- ctrl.mu.Unlock()
- defer ctrl.mu.Lock()
- if action != nil {
- action()
- }
- return rets
- }
- func (ctrl *Controller) Finish() {
- ctrl.mu.Lock()
- defer ctrl.mu.Unlock()
- // If we're currently panicking, probably because this is a deferred call,
- // pass through the panic.
- if err := recover(); err != nil {
- panic(err)
- }
- // Check that all remaining expected calls are satisfied.
- failures := false
- for _, methodMap := range ctrl.expectedCalls {
- for _, calls := range methodMap {
- for _, call := range calls {
- if !call.satisfied() {
- ctrl.t.Errorf("missing call(s) to %v", call)
- failures = true
- }
- }
- }
- }
- if failures {
- ctrl.t.Fatalf("aborting test due to missing call(s)")
- }
- }
|