Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mock return value provider function does not execute when the function doesn't return anything #506

Open
garysteinmetz opened this issue Sep 9, 2022 · 4 comments

Comments

@garysteinmetz
Copy link

(Software Versions)

% go version       
go version go1.18.2 darwin/amd64

% mockery --version
09 Sep 22 10:36 EDT INF Starting mockery dry-run=false version=v2.14.0
v2.14.0

(Description)
Mock return value provider function does not execute when the function doesn't return anything (returns void). The function will be marked as called (as defined by the AssertNumberOfCalls method), but its commands won't actually be executed.

(Sample Source Files)

Create this 'sample.go' file in the 'sample' directory.

package sample

/*
Generate mock structs of these two interfaces with the following two commands -

mockery --dir=sample --filename=StructWithVoidReturn.go --output=mocks --outpkg=mocks --name=StructWithVoidReturn
mockery --dir=sample --filename=StructWithErrorReturn.go --output=mocks --outpkg=mocks --name=StructWithErrorReturn

*/
type StructWithVoidReturn interface {
	SomeCallWithVoidReturn()
}
type StructWithErrorReturn interface {
	SomeCallWithErrorReturn() error
}

Note - As the comment in this file indicates, create the two mock files as indicated in the block comment within the above file.

(Sample Test Files)

Create this 'sample_test.go' file in the 'sample' directory.

package sample

import (
	"testing"

	"github.com/iStreamPlanet/mediaflow-base/v2/mocks"
)

func TestStructWithVoidReturn(t *testing.T) {
	structWithVoidReturn := mocks.StructWithVoidReturn{}
	structWithVoidReturn.On("SomeCallWithVoidReturn").Return(
		func() {
			t.Logf("// 'SomeCallWithVoidReturn' mock function has been called")
		},
	)
	structWithVoidReturn.SomeCallWithVoidReturn()
	structWithVoidReturn.AssertNumberOfCalls(t, "SomeCallWithVoidReturn", 1)
}

func TestStructWithErrorReturn(t *testing.T) {
	structWithErrorReturn := mocks.StructWithErrorReturn{}
	structWithErrorReturn.On("SomeCallWithErrorReturn").Return(
		func() error {
			t.Logf("// 'SomeCallWithErrorReturn' mock function has been called")
			return nil
		},
	)
	structWithErrorReturn.SomeCallWithErrorReturn()
	structWithErrorReturn.AssertNumberOfCalls(t, "SomeCallWithErrorReturn", 1)
}

(Sample Execution)
Now run command go test ./... -tags=unit_test -v .

(Supporting Evidence)
Notice that // 'SomeCallWithErrorReturn' mock function has been called is present in the output but // 'SomeCallWithVoidReturn' mock function has been called is not. Also notice that these two test functions successfully call AssertNumberOfCalls to confirm that the respective function that each is testing has been called exactly once.

(Supplemental Evidence)
Studying the mock struct files themselves reveals that they have a different implementation for their respective function.

//StructWithVoidReturn.go

// SomeCallWithVoidReturn provides a mock function with given fields:
func (_m *StructWithVoidReturn) SomeCallWithVoidReturn() {
	_m.Called()
}

//StructWithErrorReturn.go

// SomeCallWithErrorReturn provides a mock function with given fields:
func (_m *StructWithErrorReturn) SomeCallWithErrorReturn() error {
	ret := _m.Called()

	var r0 error
	if rf, ok := ret.Get(0).(func() error); ok {
		r0 = rf()
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

Note how the SomeCallWithVoidReturn function does not contain the if-else block nor does it contain a rf() command like the SomeCallWithErrorReturn function.

@LandonTClipp
Copy link
Contributor

This looks like a legitimate issue. Without digging too deep into the code right now, it seems like this should be an easy fix (hopefully just changing some conditional branch in the generation code?).

@JamesOB-WPE
Copy link

Is there a reason for using .Return over .Run in the SomeCallWithVoidReturn scenario? Unless I'm missing something, it seems designed for this purpose.

@garysteinmetz
Copy link
Author

A conjecture could be made that while one function ('Run') may be better than another ('Return'), the ('Return') function should support all of its documented use cases (which presumably includes the case where a function doesn't return anything).

@dlwyatt
Copy link
Contributor

dlwyatt commented Feb 5, 2023

Return only has any meaning if you're mocking a function that has return parameters. If there's nothing to return, Run seems like the best choice. (It would be bad for the mock to try to call ret.Get(0) when the mocked function has no return parameters; that would panic 99% of the time.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants