Documentation ¶
Overview ¶
Package pers (hel...pers ... get it? Yeah, I know, I have a love/hate relationship with myself too) contains a bunch of helpers for working with hel mocks. From making a mock consistently return to matchers - they'll all be here.
Index ¶
- Constants
- func BlockReturns(t T, mock any, methodName string) (unblock func())
- func ConsistentlyReturn(t T, mock any, args ...any) (stop func())
- func Go[T any](t TestingT, f func() (T, error)) (<-chan T, <-chan error)
- func MethodWasCalled(t TestingT, mock any, name string, opts ...HaveMethodExecutedOption)
- func MethodWasNotCalled(t TestingT, mock any, name string, opts ...HaveMethodExecutedOption)
- func Panic(mock any, withValue any)
- func Return(mock any, args ...any)
- type HaveMethodExecutedMatcher
- type HaveMethodExecutedOption
- type OnparMatcher
- type Sequence
- type T
- type TestingT
Examples ¶
Constants ¶
const Any argAny = -1
Any is a special value to tell pers to allow any value at the position used. For example, you can assert only on the second argument with:
HaveMethodExecuted("Foo", WithArgs(Any, 22))
const VariadicAny variadicAny = -2
VariadicAny is a special value, similar to Any, but specifically to tell pers to allow any number of values for the variadic arguments. It must be passed in after all non-variadic arguments so that its position matches the position that variadic arguments would normally be.
This cannot be used to check some variadic arguments without checking the others - at least right now, you must either assert on all of the variadic arguments or none of them.
Variables ¶
This section is empty.
Functions ¶
func BlockReturns ¶ added in v0.5.0
BlockReturns blocks a mock method from returning until the returned unblock function is called. This may be used to pause ConsistentlyReturn or just to prevent a method from returning before a test has finished checking the application state.
func ConsistentlyReturn ¶
ConsistentlyReturn will continue adding a given value to the channel until the test is done or the returned stop function is called, whichever happens first. When ConsistentlyReturn stops adding values to the channel(s), it will drain those channels before returning.
Mock method calls may be temporarily paused using BlockReturns, which prevents ConsistentlyReturn from returning until unblocked.
After the first call to stop (or after the test completes), calls to stop will be a no-op.
The value for mock may be either a channel or a struct full of channels (usually a return struct from a generated mock).
ConsistentlyReturn will panic if:
- args contains a different number of arguments than the number of channels on mock.
- any of the arguments passed in are not compatible to the return types of mock.
func Go ¶ added in v0.6.5
Go helps perform concurrent testing. In some cases, it's useful for a test to take complete control over the flow of business logic - for example, to test how the code handles timeouts or to perform checks before allowing logic to resume.
In these cases, it's common to kick off business logic in a separate goroutine. The business logic will block each time it calls a method on a hel mock, which allows us to perform assertions while the business logic is paused, then tell it to resume by telling the mock to return.
Go is provided mainly to document this pattern; but also it helps make some of the setup simpler.
See the examples for detail.
Example ¶
package main import ( "fmt" "time" "git.sr.ht/~nelsam/hel/pkg/pers" "git.sr.ht/~nelsam/hel/vegr" "git.sr.ht/~nelsam/hel/vegr/ret" ) type mockSomeType struct { t vegr.TestingT timeout time.Duration SomeMethodCalled chan bool SomeMethodInput struct { Arg0 chan string Arg1 chan string } SomeMethodOutput struct { MockReturnBlocker ret.Blocker MockPanicker ret.Panicker Ret0 chan string } SomeOtherMethodCalled chan bool SomeOtherMethodOutput struct { MockReturnBlocker ret.Blocker MockPanicker ret.Panicker } } func newMockSomeType(t vegr.TestingT, timeout time.Duration) *mockSomeType { m := &mockSomeType{t: t, timeout: timeout} m.SomeMethodCalled = make(chan bool, 100) m.SomeMethodInput.Arg0 = make(chan string, 100) m.SomeMethodInput.Arg1 = make(chan string, 100) m.SomeMethodOutput.MockReturnBlocker = vegr.BlockChan() m.SomeMethodOutput.MockPanicker = make(ret.Panicker, 100) m.SomeMethodOutput.Ret0 = make(chan string, 100) m.SomeOtherMethodCalled = make(chan bool, 100) m.SomeOtherMethodOutput.MockReturnBlocker = vegr.BlockChan() m.SomeOtherMethodOutput.MockPanicker = make(ret.Panicker, 100) return m } func (m *mockSomeType) SomeMethod(arg0 string, arg1 string) (ret0 string) { m.t.Helper() m.SomeMethodCalled <- true m.SomeMethodInput.Arg0 <- arg0 m.SomeMethodInput.Arg1 <- arg1 vegr.PopulateReturns(m.t, "SomeMethod", m.timeout, m.SomeMethodOutput, &ret0) return ret0 } func (m *mockSomeType) SomeOtherMethod() { m.t.Helper() m.SomeOtherMethodCalled <- true vegr.PopulateReturns(m.t, "SomeOtherMethod", m.timeout, m.SomeOtherMethodOutput) } type printingT struct{} func (t printingT) Fatalf(s string, args ...any) { fmt.Printf(s+"\n", args...) } func (t printingT) Logf(s string, args ...any) { fmt.Printf(s+"\n", args...) } func (t printingT) Failed() bool { return false } func (t printingT) Helper() { } func (t printingT) Cleanup(func()) { } type SomeType interface { SomeMethod(string, string) string SomeOtherMethod() } func someFunc(v SomeType, args ...string) (string, int, error) { defer v.SomeOtherMethod() switch len(args) { case 0: return v.SomeMethod("", ""), 0, nil case 1: return v.SomeMethod(args[0], ""), 1, nil case 2: return v.SomeMethod(args[0], args[1]), 2, nil default: return "", -1, fmt.Errorf("someFunc: unsupported number of arguments: %d", len(args)) } } func main() { var t printingT someMock := newMockSomeType(t, time.Second) type someResult struct { result string n int } // equivalent code without using Go: // // results := make(chan someResult, 1) // errs := make(chan error, 1) // go func() { // res, n, err := someFunc(someMock, someArgs...) // errs <- err // results <- someResult{res, n} // }() results, errs := pers.Go(t, func() (someResult, error) { result, n, err := someFunc(someMock, "foo") return someResult{result, n}, err }) // At this point, the business logic is running in a separate goroutine. // If it calls a method on a mock, it will block waiting for a return. // Let's set up an expected method call and provide a return value. pers.MethodWasCalled(t, someMock, "SomeMethod", pers.Within(time.Second), pers.WithArgs("foo", ""), pers.Returning("baz"), ) // Within tells the mock to give the business logic time to catch up. Then // we check the parameters that SomeMethod was called with before // returning "baz". // If we get to this point, then we know that the business logic called // SomeMethod as we expected it to, and we've returned the value we want to // the business logic. Now we can set up another check. pers.MethodWasCalled(t, someMock, "SomeOtherMethod", pers.Within(time.Second), // even if SomeOtherMethod doesn't have return values, the mock will // block until we tell it to return. pers.Returning(), ) // Eventually, the business logic will return, and we can check those // values here. err := <-errs res := <-results if err != nil { t.Fatalf("error was non-nil: %v", err) } if res.result != "baz" { t.Fatalf("result was not baz: %v", res) } t.Logf("success") }
Output: success
func MethodWasCalled ¶ added in v0.6.3
func MethodWasCalled(t TestingT, mock any, name string, opts ...HaveMethodExecutedOption)
MethodWasCalled is an assertion function for asserting that a method in a hel mock was called. Options may be used as additional assertions about the arguments or to adjust the method's behavior (e.g. cause it to panic or pass along return values).
MethodWasCalled will panic if mock does not have a method matching name.
Certain options may trigger panics in other cases - see the documentation for those options for more detail.
func MethodWasNotCalled ¶ added in v0.6.4
func MethodWasNotCalled(t TestingT, mock any, name string, opts ...HaveMethodExecutedOption)
MethodWasNotCalled is an invers assertion function of MethodWasCalled.
func Panic ¶ added in v0.6.0
Panic enqueues a panic on a mock method call. This panic will be enqueued after any queued returns (using Return).
Panic itself will panic if mock is not a struct with a ret.Panicker field.
func Return ¶
Return will enqueue a normal, non-panicking return on a mock method.
Return panics if:
- the passed in mock value is not a valid mock field (a channel or struct of channels).
- the passed in args cannot be returned on the mock field.
- the passed in mock value is already full and sending another return value would block.
Types ¶
type HaveMethodExecutedMatcher ¶
type HaveMethodExecutedMatcher struct { MethodName string // contains filtered or unexported fields }
HaveMethodExecutedMatcher is a matcher, intended for use with packages like github.com/poy/onpar/expect, to ensure that a method on a mock was executed.
func HaveMethodExecuted ¶
func HaveMethodExecuted(name string, opts ...HaveMethodExecutedOption) *HaveMethodExecutedMatcher
HaveMethodExecuted returns a matcher, intended for use with packages like github.com/poy/onpar/expect, which asserts that the method referenced by name was executed. Options can modify the behavior of the matcher.
HaveMethodExecuted will panic if the mock does not have a method matching name.
The HaveMethodExecutedMatcher will panic if any of the options used don't match the target mock properly. Check the documentation on the options to get more specific information about what would cause the matcher to panic.
func (HaveMethodExecutedMatcher) Match ¶
func (m HaveMethodExecutedMatcher) Match(v any) (any, error)
Match checks the mock value v to see if it has a method matching m.MethodName which has been called.
func (*HaveMethodExecutedMatcher) UseDiffer ¶
func (m *HaveMethodExecutedMatcher) UseDiffer(d matchers.Differ)
UseDiffer sets m to use d when showing a diff between actual and expected values.
type HaveMethodExecutedOption ¶
type HaveMethodExecutedOption func(HaveMethodExecutedMatcher) HaveMethodExecutedMatcher
HaveMethodExecutedOption is an option function for the HaveMethodExecutedMatcher.
func Returning ¶
func Returning(vals ...any) HaveMethodExecutedOption
Returning returns a HaveMethodExecutedOption which will return the arguments on the mock's return channels after the method in question has been called.
Returning will cause a panic if:
- The values provided are not ConvertibleTo the mock's return types.
- The number of values provided does not match the number of return types in the mock.
- For variadic methods, the matcher will use vals[len(nonVariadicArgs(mock)):] as the variadic argument.
func StoreArgs ¶
func StoreArgs(targets ...any) HaveMethodExecutedOption
StoreArgs returns a HaveMethodExecutedOption which stores the arguments passed to the method in the addresses provided. A nil value tells the matcher to skip the argument at that index.
StoreArgs will cause a panic if: - The values provided are not pointers. - The mock's arguments are not ConvertibleTo the targets' types. - The number of targets does not match the number of arguments in the function.
- For variadic methods, a target value must be provided to store the variadic values. If the value passed in for the variadic values is nil, it will be skipped as normal. Otherwise, it must be a pointer to a slice capable of storing the variadic arguments' type.
Example ¶
package main import ( "fmt" "git.sr.ht/~nelsam/hel/pkg/pers" ) type fakeMock struct { FooCalled chan struct{} FooInput struct { Arg0 chan int Arg1 chan string } FooOutput struct { Err chan error } BarCalled chan struct{} } func newFakeMock() *fakeMock { m := &fakeMock{} m.FooCalled = make(chan struct{}, 100) m.FooInput.Arg0 = make(chan int, 100) m.FooInput.Arg1 = make(chan string, 100) m.FooOutput.Err = make(chan error, 100) m.BarCalled = make(chan struct{}, 100) return m } func (m *fakeMock) Foo(arg0 int, arg1 string) error { m.FooCalled <- struct{}{} m.FooInput.Arg0 <- arg0 m.FooInput.Arg1 <- arg1 return <-m.FooOutput.Err } func (m *fakeMock) Bar() { m.BarCalled <- struct{}{} } func main() { // Simulate calling a method on a mock fm := newFakeMock() fm.FooCalled <- struct{}{} fm.FooInput.Arg0 <- 42 fm.FooInput.Arg1 <- "foobar" // Provide some addresses to store the arguments var ( arg0 int arg1 string ) m := pers.HaveMethodExecuted("Foo", pers.StoreArgs(&arg0, &arg1)) _, err := m.Match(fm) fmt.Println(err) fmt.Println(arg0) fmt.Println(arg1) }
Output: <nil> 42 foobar
func WithArgs ¶
func WithArgs(args ...any) HaveMethodExecutedOption
WithArgs returns a HaveMethodExecutedOption which sets the HaveMethodExecutedMatcher to only pass if the latest execution of the method called it with the passed in arguments.
WithArgs will cause a panic if:
- The argument passed in is not ConvertibleTo the mock's argument type at the matching index.
- The number of arguments does not match the number of arguments in the mock.
- For variadic methods, the number of arguments in the mock is a minimum. If len(args) is >= len(nonVariadicArgs(mock)) but len(args) != len(totalArgs(mock)), the matcher will return an error instead.
func Within ¶
func Within(d time.Duration) HaveMethodExecutedOption
Within returns a HaveMethodExecutedOption which sets the HaveMethodExecutedMatcher to be executed within a given timeframe.
type OnparMatcher ¶ added in v0.6.5
OnparMatcher is any matcher implementing onpar's matcher type. Some code in this package supports matching against child matchers, for example:
HaveBeenExecuted("Foo", WithArgs(matchers.HaveLen(12)))
type Sequence ¶ added in v0.6.5
type Sequence struct {
// contains filtered or unexported fields
}
Sequence is a helper for tracking a sequence of calls on various hel mocks. It must be constructed with CallSequence so that assertions will be checked during test cleanup.
Sequence does not check that calls to different mocks happen in exactly the order that the assertions are in. If that level of control is required, we recommend following the concurrent testing pattern, described in the documentation on `Go`.
Sequence will panic if the mock's return channel is already full.
func CallSequence ¶ added in v0.6.5
CallSequence returns a new mock call sequence tracker.
func (*Sequence) Expect ¶ added in v0.6.5
func (s *Sequence) Expect(mock any, methodName string, opts ...HaveMethodExecutedOption)
Expect adds an expected method call to the sequence.
Options intended for applying assertions against concurrent code (like Within) are ignored.
If no Returning option is used, Expect will automatically return the zero value for each return type on the method.