Spring MVC: Testing particular annotated method gets invoked - java

In Spring MVC 3.0, how do I test that a particular method gets invoked?
For example, I have this controller method:
public class myController {
#RequestMapping(value = "/create", method = RequestMethod.GET)
public ModelAndView create(ModelMap map) {
map.put("category", new Category());
return new ModelAndView("views/someView", map);
}
}
How do I test that this create() method gets called when somebody requests http://example.com/create url.

In Unit Tests, you should only test your controller's Java code, without using any Servlet technology.
In integration tests you can do one of several things:
Use the org.springframework.mock.web package in the spring-test artifact, which contains Mock Objects for request, response, servletContext to fire fake requests at your controllers and read the data from the fake responses.
Or use a web testing framework like Selenium that works against a deployed webapp.

How do I test that this create() method gets called when somebody requests http://example.com/create url.
Looks really like a integration test. Sean Patrick Floyd already mentioned some ways how to test that, but to my understanding none of this options really tests that a request to an url really invokes the method. -- The mocking way simulates the request and the selenium test tests only the return value, but not the Invocation. -- Don't get my wrong, I believe that this two other tests are in most cases better (easyer to test and even more valid test results), but if you really want to test the invokation I would come up with this solution.
Use a web testing framework like Selenium (or Selenium 2/Webdriver) or only a simple one that only generates HTTP requests. -- To do that tests you will need of curse the deployed application. -- so it is realy an integration test.
To check that the method is invoked. I would recommend to use a logging tool (Log4J). Then use a Tool like AspectJ or Spring AOP Support to add logging statements to your controller method. The logging should be written to some logger that writes to an other destination then the other loggers you use.
At the end the last step is, that you need to verify that the expected logging statement is is the logfile after the test sends the http request. (Pay attention to the fact, that the logging may is asynchronous.)

Related

how can you test this web layer in junit?

how can you test this web layer in junit ?
#GetMapping("/")
#ResponseBody
public String index() {
Map<String, Object> context = Maps.newHashMap();
context.put("detail", "hello!");
return templateRender.render(getTemplate("index.html"), context);
}
Just know, this is no longer unit testing but integration testing which is by definition is a phase in software testing in which individual software modules are combined and tested as a group. In this case, you test the integration between the layers - the controller and the service one.
Here you have not many choices to do. You can either:
Trust that the method render(..) does the job well and is already tested by its provider. I'd rather focus on the getTemplate(..) method whether it returns a correct template.
Use a front-end automatized test to call the URL and detect a returned webpage according to given parameters. Selenium or Robot framework is capable to do so. Smoke and sanity tests cover this up as well and server for this purpose.
Use a third-party library as already suggested. However, I see no real benefit aside of increasing the code-coverage metrics (be careful - it might be a false-positive indicator).
From what I understand you need to test the web page via JUnit. You can use JWebUnit:
#Before
public void prepare() {
setBaseUrl("http://localhost:8080/test");
}
#Test
public void testLogin() {
beginAt("/home");
clickLink("login");
assertTitleEquals("Login");
}
}
Source:
https://jwebunit.github.io/jwebunit/

How to make Spring Cloud Contract reset WireMock before or after each test

We are writing a Spring Boot application and use the Cloud Contract WireMock support to stub a backing service. Our test class is annotated like so:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureWireMock(port = 0)
public class Tests...
This works fine except for one thing: We found out that Spring Cloud does not seem to reset WireMock, in particular delete stubs, in between tests so that tests are not isolated properly. Of course, you can accomplish this yourself with a #Before method containing a reset(), but we wonder whether this is intentional. Is there an option that we have overlooked or an additional annotation one has to use?
After all, it is not possible to define stubs in a #BeforeClass method that would be gone if a reset would always be performed, so we wonder what speaks against doing it out of the box?
Configure Spring Boot property:
wiremock:
reset-mappings-after-each-test: true
ref: https://github.com/spring-cloud/spring-cloud-contract/commit/67119e62f6b30da56b06aade87ec3ba61de7fd24
I ended up injecting WireMockServer and running wireMockServer.resetAll() in #BeforeEach.
The WireMock server can be reset at any time, removing all stub mappings and deleting the request log. If you’re using either of the JUnit rules this will happen automatically at the start of every test case. However you can do it yourself via a call to WireMock.reset() in Java or sending a POST request with an empty body to http://<host>:<port>/__admin/reset.
To reset just the stub mappings leaving the request log intact send a DELETE to http://<host>:<port>/__admin/mappings.
Hope this is useful.

In an Apache Camel application, how can unit tests inject mock endpoints in place of real ones?

I am implementing a message translator pattern with Apache Camel, to consume messages from a RESTful endpoint and send them onward to an AMQP endpoint.
The enclosing application is based on Spring Boot, and so I'm using Camel's "spring-boot" component to integrate the two frameworks. As suggested by the documentation in this spring-boot link, I'm implementing my Camel route inside of a #Configuration-annotated class which extends RouteBuilder:
#Component
public class MyRestToAmqpRouter extends RouteBuilder {
#Override
public void configure() throws Exception {
from("jetty:http://my-restful-url")
.process(exchange -> {
// convert the message body from JSON to XML, take some
// incoming header values and put them in the outgoing
// body, etc...
}).to("rabbitmq://my-rabbitmq-url");
}
}
My question involves how to go about unit-testing this translation, without needing an actual RESTful endpoint or configured RabbitMQ broker? I've read many online examples, as well as the Camel in Action book... and it seems like the typical approach for unit testing a Camel route is to cut-n-paste the route into your unit test, and replace one or more endpoint URL's with "mock:whatever".
I guess that sorta works... but it's awfully brittle, and your test suite won't recognize when someone later changes the real code without updating the unit test.
I've tried to adapt some Spring-based unit testing examples with mocks, like this:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {Application.class})
public class MyRestToAmqpRouterTest extends AbstractJUnit4SpringContextTests {
#Produce(uri = "jetty:http://my-restful-url")
private ProducerTemplate fakeRest;
#EndpointInject(uri = "rabbitmq://my-rabbit-url")
private MockEndpoint fakeRabbit;
#Test
#DirtiesContext
public void testRouter() throws InterruptedException {
fakeRabbit.expectedMessageCount(1);
fakeRest.sendBodyAndHeader("", "header-1", "some value");
fakeRabbit.assertIsSatisfied();
}
}
My hope was that Camel would take those endpoint URLs from the unit test, register them as mocks... and then use the mocks rather than the real endpoint when the real code tries to use those URLs.
However, I'm not sure that this is possible. When I use the real URLs in the unit test I get IllegalArgumentException's, because you apparently can't inject a "real" endpoint URL into a MockEndpoint instance (only URLs prefixed with "mock:").
When I do use a "mock:..." endpoint URL in my unit test, then it's useless because there's nothing tying it to the real endpoint URL in the class under test. So that real endpoint URL is never overridden. When the real code is executed, it just uses the real endpoint as normal (and the goal is to be able to test without an external dependency on RabbitMQ).
Am I missing something on a really fundamental level here? It seems like there would be a way for unit tests to inject fake routes into a class like this, so that the code under test could switch from real endpoints to mock ones without even realizing it. Alternatively, I suppose that I could refactor my code so that the anonymous Processor were elevated to a standalone class... and then I could unit test its translation logic independently of the route. But that just seems like an incomplete test.
Some pointers what you may do.
You can read the Camel book again about testing, and pay attention to using advice with
http://camel.apache.org/advicewith.html.
And there is also mockEndpointsAndSkip
http://camel.apache.org/mock.html
And you can also use the stub component
http://camel.apache.org/stub
Or use property placeholders in your routes, and then configure the uris to be mock/stub etc for testing, and use the real ones for production
http://camel.apache.org/using-propertyplaceholder.html

Spring Integration unit test http:outbound-gateway

Trying to figure out how to best unit test an http:outbound-gateway in a Spring Integration workflow.
Here's what our gateway looks like:
<int-http:outbound-gateway id="gateway"
request-channel="registrationQueue"
message-converters="jsonMessageConverter"
url-expression="#urlGenerator.resolve()"
http-method="POST"
expected-response-type="javax.ws.rs.core.Response"
reply-channel="nullChannel"
error-handler="httpResponseErrorHandler"/>
Specifically, we want to..
Assert serialization of the objects being sent; do the message-converters correctly process messages coming from the request-channel?
Verify response handling from the 3rd party service; what is the behavior given various responses (expected & unexpected) and errors (internal & external)?
We've got a number of unit tests that mock out the end points and assert the steps of our integration workflow behave as expected. Something like the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:test-config.xml"})
public class FileRegistrationWorkflowTest {
...
#Autowired
private MessageChannel fileFoundChannel;
#Autowired
private QueueChannel testRegistrationQueue;
...
#Test
public void shouldQueueRegistrationForFileWithEntityId() {
// Given
mockFileLookupService(FILE_ID, FILENAME_WITH_ENTITY_ID);
// When
fileFoundChannel.send(MessageBuilder.withPayload(FILE_ID).build());
// Then
Message<?> message = testRegistrationQueue.receive();
assertThat(message, hasPayload(expected));
}
}
This method of testing works great for the steps along the workflow. Our trouble is testing the the end point gateways..
We can't mock the http:outbound-gateway, then we aren't testing it.
We don't want to deploy a real HTTP service to interact with, that's more an integration test.
The 3rd party service is only resolved by the url-expression, so there isn't a Spring bean to mock out.
Perhaps we can intercept the HTTP request Spring tries to send?
In the framework tests we use a DirectFieldAccessor to replace the endpoint's RestTemplate with a mock (actually a stub). However, this doesn't test the converters.
You can get even more sophisticated, where the real RestTemplate can be tested; just get a reference to it (with the SI TestUtils.getPropertyValue() or a DirectFieldAccessor) and configure it as discussed in the Spring Framework documentation.
You can get a reference to the handler with bean name endpointId.handler.

How do I unit test code which calls the Jersey Client API?

I wrote code which calls the Jersey client API which in turn calls a web service which is out of my control. I do not want my unit test to call the actual web service.
What is the best approach for writing a unit test for code which calls the Jersey client API? Should I use the Jersey server API to write a JAX-RS web service and then use the Jersey Test Framework for the unit test? Or should I mock out the Jersey web service calls? I have access to JMock. Or should I try another approach?
During my research, I found this discussion describing various options, but I did find a complete solution. Are there any code examples available showing a suggested JUnit approach? I could not find any in the Jersey documentation.
Here is the relevant source code:
public String getResult(URI uri) throws Exception {
// error handling code removed for clarity
ClientConfig clientConfig = new DefaultClientConfig();
Client client = Client.create(clientConfig);
WebResource service = client.resource(uri);
String result = service.accept(accept).get(String.class);
return result;
}
Here are examples of test code I would like to pass. I would like to test (1) passing in a valid URI and getting a valid string back and (2) passing in an invalid (for whatever reason -- unreachable or unauthorized) URI and getting an exception back.
#Test
public void testGetResult_ValidUri() throws Exception {
String xml = retriever.getResult(VALID_URI);
Assert.assertFalse(StringUtils.isBlank(xml));
}
#Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
retriever.getResult(INVALID_URI);
}
Everything above is the simple description of what my code does. In reality, there is a layer on top of that that accepts two URIs, first tries calling the first URI, and if that URI fails then it tries calling the second URI. I would like to have unit tests covering (1) the first URI succeeds, (2) the first URI fails and the second URI succeeds, and (3) both URIs fail. This code is sufficiently complex that I want to test these different scenarios using JUnit, but to do this I either need to run actual stand-in web services or mock out the Jersey client API calls.
Try to use Mockito or Easymock for mocking service calls. You need to mock only these methods which are actually used - no need to mock every method. You can creat mock object for WebResource class, then mock accept method call.
In #BeforeClass/#Before JUnit test method write something like (Mockito example)
WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);
Then in your tests you can use res object as if it was real object and call mock method on it. Instead of returning value you can also throw exceptions. Mockito is pretty cool.
Typically what you are really after is "does the way I use the Jersey Client DSL produce a request to the correct URL with the correct payload and URL parameters". Testing this with Mockito is really verbose and the setup code will usually end up looking something like this:
when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
when(testAuthentication.request(
MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());
And this is basically just for a single REST request. It's overly verbose, and instead of testing the hoped outcome you are just replicating the steps you think are correct in using the Jersey Client DSL.
Instead of the above, I would aim for mocking a simple service. For this I've used WireMock which starts a Jetty server and where I can stub things like "expect a request to this URL, respond with this message and verify that the payload is this".
I know this is edging on an integration test and it is a bit slower than just using Mockito but I value testing the real outcome and I value the readability of the tests way more in this case.
Setup for a WireMock based Jersey Client test looks something like this:
#Test
public void exactUrlOnly() {
stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
assertThat(testClient.get("/some/thing").statusCode(), is(200));
assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
Just implement a work-alike service and in your unit test setup start the service using HttpServerFactory.

Categories

Resources