I am implementing a API in Golang. I have a endpoint where I am calling a method with parameters of other package. Now I need to check that, that method has been called in the request.
Below is the small similar scenario what I am doing and what I am expecting.
My handler
package myPackage
import (
"log"
"github.com/myrepo/notifier" // my another package
)
func MyHandler(writer http.ResponseWriter, request *http.Request) {
// ...
// ...
notifier.Notify(4, "sdfsdf")
// ...
// ...
}
Testing handler
func TestMyHandler(t *testing.T) {
// Here I want to
req, _ := http.NewRequest("GET", "/myendpoint", nil)
// ... Want to test that notifier.Notify is called
// ...
}
In the TestMyHandler, I want to check that notifier.Notify has called.
Findings
I tried to understand the AssertNumberOfCalls, func (*Mock) Called, and func (*Mock) MethodCalled but I am not sure how to use them :(.
I am a newbie in Golang and really exicted to do that.
Please let me know if I missed anything or you may need more information for more understaing.
This is a good opportunity to use dependency injection and interfaces.
Namely, we need to extract the concept of a Notifier
(warning: code not tested directly)
type Notifier interface {
Notify(int, string)() error
}
Now to avoid any confusion with the notifier library, use a local alias.
import "github.com/myrepo/notifier" mynotifier
Then, because the library you're using exports it as a function, not within a struct, we'll need to make a struct that implements our interface
type myNotifier struct {}
func (mn *myNotifier) Notify(n int, message string) error {
return mynotifier.Notify(n, message)
}
Then you modify your function:
func MyHandler(writer http.ResponseWriter, request *http.Request, notifier Notifier) {
// ...
// ...
notifier.Notify(4, "sdfsdf")
// ...
// ...
}
Then in your test, you're now free to send in a spy Notifier
type spyNotifier struct {
called boolean
}
func (n *spyNotifier) Notify(n int, msg string) error {
n.called = true
return
}
Want to test that notifier.Notify is called.
No you don't. You are interested in that the handler does what it should do and this seems to consist of two things:
Return the right response (easy to test with a net/http/httptest.ResponseRecorder), and
Has some noticeable side effect, here issue some notification.
To test 2. you test that the notification was issued, not that some function was called.
Whatever notify.Notify results in (e.g. a database entry, a file, some HTTP call) should be tested. Formally this is no longer unit testing but testing for side effects is never strict unit testing.
What you can do: Wrap your handler logic into some object and observe that objects state. Ugly. Don't.
This approach is similar to Mathew's answer, but uses the mock package from testify instead. Here, you create a mock implementation of the Notifier, register the method call, and assert that the method has been called with the expected arguments.
Implementation
package handler
import (
"net/http"
"github.com/stretchr/testify/mock"
)
// the interface for the Notifier
type Notifier interface {
Notify(int, string) error
}
// the mock implementation of the interface above
type MockNotifier struct {
mock.Mock
}
// stub the notify method to ensure it can be expected later in the test
func (mockNotifier *MockNotifier) Notify(arg1 int, arg2 string) error {
args := mockNotifier.Called(arg1, arg2)
return args.Error(0)
}
// this handler which accepts a Notifier for dependency injection
type Handler struct {
notifier Notifier
}
// the MyHandler implementation which calls the notifier instance
func (h *Handler) MyHandler(writer http.ResponseWriter, request *http.Request) {
// this is what we want to test!
h.notifier.Notify(4, "sdfsdf")
}
Test
package handler_test
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestMyHandler(t *testing.T) {
t.Parallel()
mockNotifier := MockNotifier{}
handler := Handler{ notifier: &mockNotifier }
// register the mock to expect a call to Notify with the given arguments and return nil as the error
mockNotifier.On("Notify", 4, "sdfsdf").Return(nil)
// setup the test arguments
request := httptest.NewRequest(http.MethodGet, "/someapi", nil)
writer := httptest.NewRecorder()
// call the handler
handler.MyHandler(writer, request)
// this is the important part!!
// this ensures that the mock Notify method was called with the correct arguments, otherwise the test will fail
mockNotifier.AssertExpectations(t)
}
Related
I want to mock requests with ktor's http client using MockK. The problem is all the methods related to making requests with the client are inline, so I cannot use coEvery on those methods. The next thing I tried was to go through the called methods until I found a method that wasn't inline and then mock that. After stepping through some functions, the HttpClient.request() function instantiates an HttpStatement and then calls execute() on it.
public suspend inline fun HttpClient.request(
builder: HttpRequestBuilder = HttpRequestBuilder()
): HttpResponse = HttpStatement(builder, this).execute()
If I can mock the constructor and .execute() functions, I can intercept the call and return my canned response. I can then check that the builder's params are correct inside of a verify function.
mockkConstructor(HttpStatement::class)
coEvery { anyConstructed<HttpStatement>().execute() } returns mockk {
coEvery { status } returns HttpStatusCode.OK
coEvery { body<RefreshToken>() } returns RefreshToken()
}
This code takes care of intercepting the execute call. The next step would be to verify the constructor params of HttpStatement. This code to verify execute was called works:
coVerify { anyConstructed<HttpStatement>().execute() }
Next thing is to verify the constructor params. This pull request in the MockK repo describes how to verify constructors:
coVerify { constructedWith<HttpStatement>(/* Matchers here */).execute() }
Note that I have to add the .execute() or else MockK tells me I'm not verifying anything.
Missing calls inside verify { ... } block.
io.mockk.MockKException: Missing calls inside verify { ... } block.
at app//io.mockk.impl.recording.states.VerifyingState.checkMissingCalls(VerifyingState.kt:52)
at app//io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:21)
...
Ok, so just add in the matchers. However, no combination of matchers I try works. I've tried doing a bunch of constant matchers for type Any (which should match anything right?)
coVerify { constructedWith<HttpStatement>(ConstantMatcher<Any>(true))}
I've tried a matcher for HttpRequestBuilder and HttpClient
coVerify {
constructedWith<HttpStatement>(
ConstantMatcher<HttpRequestBuilder>(true),
ConstantMatcher<HttpClient>(true)
).execute()
}
And a whole slew of others. Each time, I get this error:
Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
java.lang.AssertionError: Verification failed: call 1 of 1: HttpStatement(mockkConstructor<HttpStatement>(any(), any())).execute(any())) was not called
at io.mockk.impl.recording.states.VerifyingState.failIfNotPassed(VerifyingState.kt:63)
at io.mockk.impl.recording.states.VerifyingState.recordingDone(VerifyingState.kt:42)
...
Next thing I figured I could try would be to use an answers block earlier on in order to print out the types of the parameters being passed in case I was wrong, but that also runs into the "nothing being done in every block" error.
coEvery { anyConstructed<HttpStatement>() } answers {
args.filterNotNull().map { it::class.qualifiedName }.forEach(::println)
mockk {
coEvery { execute().status } returns HttpStatusCode.OK
coEvery { execute().body<RefreshToken>() } returns RefreshToken(
accessToken = accessToken,
expiresIn = expiresIn,
)
}
}
Is there a solution to mocking the http client? Do I have to mock something even more internal? Or do I just have to stick to using the ktor MockEngine?
I am testing a coroutine that blocks. Here is my production code:
interface Incrementer {
fun inc()
}
class MyViewModel : Incrementer, CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
launch(coroutineContext) {
delay(100)
_number.tryEmit(1)
}
}
}
And my test:
class IncTest {
#BeforeEach
fun setup() {
Dispatchers.setMain(StandardTestDispatcher())
}
#AfterEach
fun teardown() {
Dispatchers.resetMain()
}
#Test
fun incrementOnce() = runTest {
val viewModel = MyViewModel()
val results = mutableListOf<Int>()
val resultJob = viewModel.getNumber()
.onEach(results::add)
.launchIn(CoroutineScope(UnconfinedTestDispatcher(testScheduler)))
launch(StandardTestDispatcher(testScheduler)) {
viewModel.inc()
}.join()
assertEquals(listOf(0, 1), results)
resultJob.cancel()
}
}
How would I go about testing my inc() function? (The interface is carved in stone, so I can't turn inc() into a suspend function.)
There are two problems here:
You want to wait for the work done in the coroutine that viewModel.inc() launches internally.
Ideally, the 100ms delay should be fast-forwarded during tests so that it doesn't actually take 100ms to execute.
Let's start with problem #2 first: for this, you need to be able to modify MyViewModel (but not inc), and change the class so that instead of using a hardcoded Dispatchers.IO, it receives a CoroutineContext as a parameter. With this, you could pass in a TestDispatcher in tests, which would use virtual time to fast-forward the delay. You can see this pattern described in the Injecting TestDispatchers section of the Android docs.
class MyViewModel(coroutineContext: CoroutineContext) : Incrementer {
private val scope = CoroutineScope(coroutineContext)
private val _number = MutableStateFlow(0)
fun getNumber(): StateFlow<Int> = _number.asStateFlow()
override fun inc() {
scope.launch {
delay(100)
_number.tryEmit(1)
}
}
}
Here, I've also done some minor cleanup:
Made MyViewModel contain a CoroutineScope instead of implementing the interface, which is an officially recommended practice
Removed the coroutineContext parameter passed to launch, as it doesn't do anything in this case - the same context is in the scope anyway, so it'll already be used
For problem #1, waiting for work to complete, you have a few options:
If you've passed in a TestDispatcher, you can manually advance the coroutine created inside inc using testing methods like advanceUntilIdle. This is not ideal, because you're relying on implementation details a lot, and it's something you couldn't do in production. But it'll work if you can't use the nicer solution below.
viewModel.inc()
advanceUntilIdle() // Returns when all pending coroutines are done
The proper solution would be for inc to let its callers know when it's done performing its work. You could make it a suspending method instead of launching a new coroutine internally, but you stated that you can't modify the method to make it suspending. An alternative - if you're able to make this change - would be to create the new coroutine in inc using the async builder, returning the Deferred object that that creates, and then await()-ing at the call site.
override fun inc(): Deferred<Unit> {
scope.async {
delay(100)
_number.tryEmit(1)
}
}
// In the test...
viewModel.inc().await()
If you're not able to modify either the method or the class, there's no way to avoid the delay() call causing a real 100ms delay. In this case, you can force your test to wait for that amount of time before proceeding. A regular delay() within runTest would be fast-forwarded thanks to it using a TestDispatcher for the coroutine it creates, but you can get away with one of these solutions:
// delay() on a different dispatcher
viewModel.inc()
withContext(Dispatchers.Default) { delay(100) }
// Use blocking sleep
viewModel.inc()
Thread.sleep(100)
For some final notes about the test code:
Since you're doing Dispatchers.setMain, you don't need to pass in testScheduler into the TestDispatchers you create. They'll grab the scheduler from Main automatically if they find a TestDispatcher there, as described in its docs.
Instead of creating a new scope to pass in to launchIn, you could simply pass in this, the receiver of runTest, which points to a TestScope.
Let's say I have a function if that is executed asynchronous as a go routine:
func f(wg *sync.WaitGroup){
defer wg.Done()
// Do sth
}
main(){
var wg sync.WaitGroup
wg.Add(1)
go f(&wg)
wg.Wait() // Wait until f is done
// ...
}
How would I create a unit test for f that makes sure wg.Done() is called?
One option is to call wg.Done() in the test directly after the f is called. If f fails to call wg.Done() the test will panic which is not nice.
Another option would be to create an interface for sync.WaitGroup but that seems a bit weird.
How would I create a unit test for f that makes sure wg.Done() is called?
Something like this:
func TestF(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
// run the task asynchronously
go f(wg)
// wait for the WaitGroup to be done, or timeout
select {
case <-wrapWait(wg):
// all good
case <-time.NewTimer(500 * time.Millisecond).C:
t.Fail()
}
}
// helper function to allow using WaitGroup in a select
func wrapWait(wg *sync.WaitGroup) <-chan struct{} {
out := make(chan struct{})
go func() {
wg.Wait()
out <- struct{}{}
}()
return out
}
You don't inspect the WaitGroup directly, which you can't do anyway. Instead you assert that the function behaves as expected, given the expected input.
In this case, the expected input is the WaitGroup argument and the expected behavior is that wg.Done() gets called eventually. What does that mean, in practice? It means that if the function is successful a WaitGroup with count 1 will reach 0 and allow wg.Wait() to proceed.
The statement defer wg.Done() at the beginning of f already makes sure that the test is resilient to errors or crashes. The addition of a timeout is simply to make sure the test will complete within a reasonable time, i.e. that it doesn't stall your test suite for too long. Personally, I prefer using explicit timeouts, either with timers or with contexts, to 1) avoid problems if someone forgets to set timeouts at the CI level, 2) make the time ceiling available to anyone who checks out the repo and runs the test suite, i.e. avoid dependencies on IDE configs or whatnot.
How can I test code in flutter, that is dependent on the path_provider plugin?
When executing tests for code that is dependent on the path_provider plugin, I get the following error:
MissingPluginException(No implementation found for method getStorageDirectory on channel plugins.flutter.io/path_provider)
package:flutter/src/services/platform_channel.dart 319:7 MethodChannel.invokeMethod
===== asynchronous gap ===========================
dart:async _asyncErrorWrapperHelper
package: mypackage someClass.save
unit_tests/converter_test.dart 19:22
main.<fn>
You need to mock all the methods called by your code being tested if it calls them and depend on their results
in your case you should mock the method getStorageDirectory() to make it return some result that satisfy your test
for more info on how to mock check this and this
A short example of how to mock:
class MyRepo{
int myMethod(){
return 0;
}
}
class MockRepo extends Mock implements MyRepo{}
void main(){
MockRepo mockRepo = MockRepo();
test('should test some behaviour',
() async {
// arrange
when(mockRepo.myMethod()).thenAnswer(1);//in the test when myMethod is called it will return 1 and not 0
// act
//here put some method that will invoke myMethod on the MockRepo and not on the real repo
// assert
verify(mockRepo.myMethod());//verify that myMethod was called
},
);
}
At the moment I try to establish best practices for unit testing go handlers.
I need to mock dependencies, but therefore I have to be able to access / mock these dependencies.
There I some solutions I do not want to consider like for example global variables / application state. Or having all handlers as functions of a struct holding the dependencies member variables.
I was kind of satisfied with a solution where I injected the needed dependencies of a handler in the following way:
func helloHandler(db *DbService) http.HandlerFunc {
return func(w http.ResponseWriter, r *httpRequest) {
// handler code goes here
}
}
Then I am able to provide this handler for routing:
http.HandleFunc("/hello", helloHander(myDbService))
I can also test it easily:
helloHandler(myDbService)(req, respRecorder)
But when I use for example gorilla mux (what I haven't considered from the beginning) I get another problem.
Gorilla/mux adds extra semantics like filtering by Method (GET / POST), providing path parameters etc.
So I should need to test the resulting gorilla router.
But then I am again not able to inject my mock dependencies anymore.
When I get the router back everything (dependencies) is already set up.
But I also don't want to rebuild my Router in my tests because of DRY!
So for now I don't really have a good solution for the problem of conveniently setting up my routes but also being able to mock the dependencies.
Any idea?
I personally changed my Router function into a method with a Receiver, so I can create Mock receivers:
Production code:
func main(){
router := mux.NewRouter()
version.AddRouter(router, contextRoot)
someStruct := SomeStruct{SomeDependency{}}
router.HandleFunc(contextRoot, someStruct.HandleRequests).
Methods(http.MethodGet)
}
func (s SomeStruct) HandleRequests(writer http.ResponseWriter, reader *http.Request) {
...
}
func (s SomeDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
Unit test:
type mockWriter struct {}
type someMockDependency struct {}
func Test_HandleRequest1(t *testing.T) {
someStructMockDeps := SomeStruct{someMockDependency{}}
someStructMockDeps.HandleRequests(mockWriter{}, &http.Request{URL: &url.URL{RawQuery:"http://dummy-query.com"}});
assert.Equal(...)
}
func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
Integration test:
func TestHandlerFuncIntg(t *testing.T) {
if testing.Short() {
println("skipping")
t.Skip()
}
req, err := http.NewRequest("GET", "/hello?param=value", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
someStructMockDeps := SomeStruct{someMockDependency{}}
handler := http.HandlerFunc(someStructMockDeps.HandleRequests)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.Equal(t, "This is my result", rr.Body.String())
}
func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
...
return SomeReturnStruct{}
}
SomeStruct declares the dependencies (as Dependency Injection) so they can be overridden for tests.