Source File
controller.go
Belonging Package
github.com/golang/mock/gomock
// 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.
// Package gomock is 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"),
// )
package gomock
import (
)
// 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{})
}
// TestHelper is a TestReporter that has the Helper method. It is satisfied
// by the standard library's *testing.T.
type TestHelper interface {
TestReporter
Helper()
}
// cleanuper is used to check if TestHelper also has the `Cleanup` method. A
// common pattern is to pass in a `*testing.T` to
// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup
// method. This can be utilized to call `Finish()` so the caller of this library
// does not have to.
type cleanuper interface {
Cleanup(func())
}
// 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. Each test should create a new Controller and invoke Finish via
// defer.
//
// func TestFoo(t *testing.T) {
// ctrl := gomock.NewController(t)
// defer ctrl.Finish()
// // ..
// }
//
// func TestBar(t *testing.T) {
// t.Run("Sub-Test-1", st) {
// ctrl := gomock.NewController(st)
// defer ctrl.Finish()
// // ..
// })
// t.Run("Sub-Test-2", st) {
// ctrl := gomock.NewController(st)
// defer ctrl.Finish()
// // ..
// })
// })
type Controller struct {
// T should only be called within a generated mock. It is not intended to
// be used in user code and may be changed in future versions. T is the
// TestReporter passed in when creating the Controller via NewController.
// If the TestReporter does not implement a TestHelper it will be wrapped
// with a nopTestHelper.
T TestHelper
mu sync.Mutex
expectedCalls *callSet
finished bool
}
// NewController returns a new Controller. It is the preferred way to create a
// Controller.
//
// New in go1.14+, if you are passing a *testing.T into this function you no
// longer need to call ctrl.Finish() in your test methods.
func ( TestReporter) *Controller {
, := .(TestHelper)
if ! {
= &nopTestHelper{}
}
:= &Controller{
T: ,
expectedCalls: newCallSet(),
}
if , := isCleanuper(.T); {
.Cleanup(func() {
.T.Helper()
.finish(true, nil)
})
}
return
}
type cancelReporter struct {
t TestHelper
cancel func()
}
func ( *cancelReporter) ( string, ...interface{}) {
.t.Errorf(, ...)
}
func ( *cancelReporter) ( string, ...interface{}) {
defer .cancel()
.t.Fatalf(, ...)
}
func ( *cancelReporter) () {
.t.Helper()
}
// WithContext returns a new Controller and a Context, which is cancelled on any
// fatal failure.
func ( context.Context, TestReporter) (*Controller, context.Context) {
, := .(TestHelper)
if ! {
= &nopTestHelper{t: }
}
, := context.WithCancel()
return NewController(&cancelReporter{t: , cancel: }),
}
type nopTestHelper struct {
t TestReporter
}
func ( *nopTestHelper) ( string, ...interface{}) {
.t.Errorf(, ...)
}
func ( *nopTestHelper) ( string, ...interface{}) {
.t.Fatalf(, ...)
}
func ( nopTestHelper) () {}
// RecordCall is called by a mock. It should not be called by user code.
func ( *Controller) ( interface{}, string, ...interface{}) *Call {
.T.Helper()
:= reflect.ValueOf()
for := 0; < .Type().NumMethod(); ++ {
if .Type().Method().Name == {
return .RecordCallWithMethodType(, , .Method().Type(), ...)
}
}
.T.Fatalf("gomock: failed finding method %s on %T", , )
panic("unreachable")
}
// RecordCallWithMethodType is called by a mock. It should not be called by user code.
func ( *Controller) ( interface{}, string, reflect.Type, ...interface{}) *Call {
.T.Helper()
:= newCall(.T, , , , ...)
.mu.Lock()
defer .mu.Unlock()
.expectedCalls.Add()
return
}
// Call is called by a mock. It should not be called by user code.
func ( *Controller) ( interface{}, string, ...interface{}) []interface{} {
.T.Helper()
// Nest this code so we can use defer to make sure the lock is released.
:= func() []func([]interface{}) []interface{} {
.T.Helper()
.mu.Lock()
defer .mu.Unlock()
, := .expectedCalls.FindMatch(, , )
if != nil {
// callerInfo's skip should be updated if the number of calls between the user's test
// and this line changes, i.e. this code is wrapped in another anonymous function.
// 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test.
:= callerInfo(3)
.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", , , , , )
}
// 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.
:= .dropPrereqs()
for , := range {
.expectedCalls.Remove()
}
:= .call()
if .exhausted() {
.expectedCalls.Remove()
}
return
}()
var []interface{}
for , := range {
if := (); != nil {
=
}
}
return
}
// Finish checks to see if all the methods that were expected to be called
// were called. It should be invoked for each Controller. It is not idempotent
// and therefore can only be invoked once.
//
// New in go1.14+, if you are passing a *testing.T into NewController function you no
// longer need to call ctrl.Finish() in your test methods.
func ( *Controller) () {
// If we're currently panicking, probably because this is a deferred call.
// This must be recovered in the deferred function.
:= recover()
.finish(false, )
}
func ( *Controller) ( bool, interface{}) {
.T.Helper()
.mu.Lock()
defer .mu.Unlock()
if .finished {
if , := isCleanuper(.T); ! {
.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
}
return
}
.finished = true
// Short-circuit, pass through the panic.
if != nil {
panic()
}
// Check that all remaining expected calls are satisfied.
:= .expectedCalls.Failures()
for , := range {
.T.Errorf("missing call(s) to %v", )
}
if len() != 0 {
if ! {
.T.Fatalf("aborting test due to missing call(s)")
return
}
.T.Errorf("aborting test due to missing call(s)")
}
}
// callerInfo returns the file:line of the call site. skip is the number
// of stack frames to skip when reporting. 0 is callerInfo's call site.
func ( int) string {
if , , , := runtime.Caller( + 1); {
return fmt.Sprintf("%s:%d", , )
}
return "unknown file"
}
// isCleanuper checks it if t's base TestReporter has a Cleanup method.
func ( TestReporter) (cleanuper, bool) {
:= unwrapTestReporter()
, := .(cleanuper)
return ,
}
// unwrapTestReporter unwraps TestReporter to the base implementation.
func ( TestReporter) TestReporter {
:=
switch nt := .(type) {
case *cancelReporter:
= .t
if , := .(*nopTestHelper); {
= .t
}
case *nopTestHelper:
= .t
default:
// not wrapped
}
return
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)