I want to separate router layer from handler layer, so I don't want to use WebTestClient as suggested in this question. I do not think throwing WebTestClient at the problem is the right solution.
In my case, I am using MockServerRequest, which only allows testing for http status code and headers. There is no way to assert the body of the response.
val request = MockServerRequest.builder()
.method(HttpMethod.GET)
.uri(URI.create("/service"))
.build()
StepVerifier.create(myHandler.handleGet(request))
.assertNext { /* and assertThat(it.body()) ... */ }
.verifyComplete()
Is there a way to assert the body? If none then why and what is the alternative?
While debugging, I have noticed that I can see the body (entity). Hence, I have inspected the returned class and its hierarchy. I have realized that casting may solve the problem, and it worked (I am not sure if it is the right way though).
For the code in question:
val request = MockServerRequest.builder()
.method(HttpMethod.GET)
.uri(URI.create("/service"))
.build()
StepVerifier.create(myHandler.handleGet(request))
.assertNext { assertThat((it as EntityResponse<Fleet>).entity())
.isNotNull /* add more assertions */ }
.verifyComplete()
Related
I have a #Service which uses a WebClient to return a Mono<Something>. I want to test that the service adds the right data to the request - headers, URI, body. My test's approach is to inject a mocked WebClient.Builder into the service, and provide a WebClient with a custom exchange function:
#BeforeEach
void preProgramWebClientBuilder() {
webClient = WebClient.builder()
.baseUrl(TEST_REMOTE_BASE_URL)
.exchangeFunction(clientRequest -> {
// ...
}).build();
// webClientBuilder is the mockito-managed mock
when(webClientBuilder.baseUrl(anyString())).thenReturn(webClientBuilder);
when(webClientBuilder.build()).thenReturn(webClient);
}
This works well, for checking request host/path/query parameters/headers, with the exchange function either performing the checks itself, or setting the clientRequest aside for the test to do the checks. But I have not found a way to extract the body from the clientRequest object, to perform assertions on it.
Fully mocking the WebClient yields some ugly test initialization code, which I'd like to avoid. Is there a decent way to extract the request body from the clientRequest?
Just to mention it: I don't see how I can use a StepVerifier here. StepVerifier expects to get the Publisher to verify - and getting the body of the request as a publisher is exactly what I haven't managed to do. A WebTestClient has the same problem - I can perform a request with it, then do assertions on the response, but I can't substitute it for the regular WebClient in the service in order to perform assertions on the request emitted by the service.
My problem is specific to Go-kit and how to organize code within.
I'm trying to write a unit test for the following function:
func MakeHandler(svc Service, logger kitlog.Logger) http.Handler {
orderHandler := kithttptransport.NewServer(
makeOrderEndpoint(svc),
decodeRequest,
encodeResponse,
)
r := mux.NewRouter()
r.Handle("/api/v1/order/", orderHandler).Methods("GET")
return r
}
What would be the correct way of writing a proper unit test? I have seen examples such as the following:
sMock := &ServiceMock{}
h := MakeHandler(sMock, log.NewNopLogger())
r := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/api/v1/order/", bytes.NewBuffer([]byte("{}")))
h.ServeHTTP(r, req)
And then testing the body and headers of the request. But this doesn't seem like a proper unit test, as calls other parts of the code (orderHandler). Is it possible to just validate what's returned from MakeHandler() instead of during a request?
TL;DR: Yes, that test is in the right direction. You shouldn't try to test the
internals of the returned handler since that third party package may change in ways you didn't expect in the future.
Is it possible to just validate what's returned from MakeHandler() instead of
during a request?
Not in a good way. MakeHandler() returns an interface and ideally you'd use
just the interface methods in your tests.
You could look at the docs of the type returned by mux.NewRouter() to see if
there are any fields or methods in the concrete type that can give you the
information, but that can turn out to be a pain - both for understanding the
tests (one more rarely used type to learn about) and due to how future
modifications to the mux package may affect your code without breaking the
tests.
What would be the correct way of writing a proper unit test?
Your example is actually in the right direction. When testing MakeHandler(),
you're testing that the handler returned by it is able to handle all the paths
and calls the correct handler for each path. So you need to call the
ServeHTTP() method, let it do its thing and then test to see it worked
correctly. Only introspecting the handler does not guarantee correctness during
actual usage.
You may need to make actually valid requests though so you're able to understand
which handler was called based on the response body or header. That should
bring the test to a quite reasonable state. (I think you already have that)
Similarly, I'd add a basic sub test for each route that's added in the future.
Detailed handler tests can be written in separate funcs.
I am probably missing something simple here....
I have the following method I want to unit test:
public bool In(Request request)
{
return ("Admin" == (string)request.Session["Type"]);
}
This proved less easy than I expected, because I can't find a way to properly set up a Request object to pass to the method.
Directly newing up a Request results in an object that is not session enabled, and my crude attempts with a makeshift ConfigurableBootstrapper implementation and module were quite unsuccessful.
(I found this blog post, but with no code to illustrate the explanation, I can't make much of it; I could also find no actual examples in the AppHarbify code.)
So, what would be the easiest/recommended way to get a Request object on which I can use Session?
Thanks!
Create an interface that wraps the Request. Something like..
public interface IRequestWrapper {
Session Session { get; set; }
}
The pass this interface instead of the actual implementation to the "In" method.
public bool In(IRequestWrapper request)
{
return ("Admin" == (string)request.Session["Type"]);
}
Now in your Unit Test you would create a fake implementation of IRequestWrapper that returns a Fake Session objtect?
var sut = new SomeClass();
var result = sut.In(new FakeRequest());
Of course the FakeRequest would implement IRequestWrapper and the Session object can be stubbed to return "Admin".
There are other ways to do this, for example, using a Mock/Isolation frameworks, i.e Moq
Hope this helps at least put you in the right direction.
I have a scenario where ExceptionMapper are used in JAX-RS using RESTeasy 2.0.1.GA . This works all fine.
I'd now like to test the whole thing using RESTeasy's mock mechanism. Unfortunately my ExceptionMapper-provider is not registered. What am I missing?
POJOResourceFactory factory = new POJOResourceFactory(SomeWebResource.class);
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addResourceFactory(factory);
MockHttpRequest request = MockHttpRequest.get("url");
MockHttpResponse response = new MockHttpResponse();
// here my exception is thrown
dispatcher.invoke(request, response);
// but I expect the response to be 404 (which works outside the mock setup)
Assert.assertEquals(response.getStatus(), 404);
Okay, I found the solution. Needed to register the ExceptionMapper manually:
dispatcher.getProviderFactory().addExceptionMapper(SomeExceptionMapper.class);
After struggling with this problem for a few days now, I think it's worth mentioning what #Joe W wrote in the comment of the above answer as it's own answer:
"Note: addExceptionMapper()'s visibility was changed to protected in later versions. dispatcher.getProviderFactory().registerProvider(SomeExceptionMapper.class) works instead."
I have the following test to verify that my repository is calling it's respective session (I've rewritten it to highlight the actual problem):
[Test]
public void Why_Does_This_Fail()
{
var objectUnderTest = new SomeGenericsProblem();
var fakeSession = MockRepository.GenerateMock<ISession>();
fakeSession.Expect(s => s.Query<SomeClass>());
objectUnderTest.NotWorking<SomeClass>();
fakeSession.AssertWasCalled(t => t.Query<SomeClass>());
}
but when I run the test I get this:
System.InvalidOperationException :
Invalid call, the last call has been
used or no call has been made (make
sure that you are calling a virtual
(C#) / Overridable (VB) method).(C#) / Overridable (VB) method).
Any ideas what I'm doing wrong here? The session that I'm mocking is an interface, so it has to be virtual/overridable.
I have a feeling it has something to do with the fact that my Query method is a generic, but I don't know any other way to express what I'm trying to test.
Also, If I remove the part that sets up the expectation (i.e. this line of code:)
fakeSession.Expect(s => s.Query<SomeClass>());
I get a different exception which is equally confusing to me:
System.InvalidOperationException : No
expectations were setup to be
verified, ensure that the method call
in the action is a virtual (C#) /
overridable (VB.Net) method calloverridable (VB.Net) method call
So I figured out what was wrong.
ISession comes from NHibernate, which I probably should have mentioned.
The reason why this is cruicialy important is because
session.Query<T>
(which is what I'm trying to mock), is an EXTENSION METHOD.
Rhino Mocks apparently does not have the capability of mocking extension methods, hence why it's giving me the weird error.
So hopefully I'll have saves someone else the time and agony I've gone through in trying to figure out why my test won't pass.
The only solution that I've read about for this is to actually change the design of the extension method (which I can't do because it's part of NHibernate), or to use a different mocking framework like TypeMock.
[Test]
public void Query_WhenCalled_CallsSessionQuery()
{
// arrange
var session = MockRepository.GenerateStub<ISession>();
var r = new Repository(session);
// act
r.Query<SomeClass>();
// assert
session.AssertWasCalled(s => s.Query<SomeClass>());
}