how can you test this web layer in junit? - java

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/

Related

Is it good approach to use Mocking for integration tests?

I'm currently learning unit tests and integration tests. In my unit tests I have been using Mockito for mocking, such as when(something).thenReturn(something) etc. I used this approach to test the business logic of the application.
Now I am trying to create integration tests to test all the endpoints of my application. The layers of my application are as follows: Controller -> Interface -> Service -> Repository.
When I tried to create an integration test for the POST method that creates a user, the request should have returned the created user, but returned an empty string. The test looked like this:
#Test
void create() throws Exception {
UserCreationDTO user = new UserCreationDTO(1L, "Steve", "steve#mail.com", "password");
ObjectMapper objectMapper = new ObjectMapper();
String text = objectMapper.writeValueAsString(user);
this.mockMvc
.perform(
MockMvcRequestBuilders.post("/users").contentType("application/json").content(text))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(1))
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Steve"))
.andExpect(MockMvcResultMatchers.jsonPath("$.email").value("steve#mail.com"))
.andExpect(MockMvcResultMatchers.jsonPath("$.password").value("password"));
}
I later found out that I need to add a line of code to mock the userInterface and what it should return.
Mockito.when(userInterface.create(Mockito.any())).thenReturn(user);
After adding it, the method started returning the created user and the test passed. After reading a couple of articles, I came across an approach that says an integration test should not contain mocking.
So my question is: Is this approach I have taken correct and can it be considered a correct integration test or do I need to get rid of the mocking of userInterface? In case I have to remove the mocking, how can I ensure that the test passes and returns not just an empty string, but an actually created user?

In Spring Boot what should I test in rest controller?

I have a hypothetical rest end point.
#GetMapping(value = "/happy/{happyId}",
produces = MediaType.APPLICATION_JSON_VALUE)
public Response<?> getHappy(#PathVariable Long happyId) {
Response response = new Response();
response.update(happyService.getById(happyId));
return response;
}
In this code, happyService could throw UnhappyException if id does not exist, and this code is tested in another place, eg) HappyServiceTest.
Now, let say if I want to test my rest controller, should I also be testing the exception flow? Or is this unnecessary?
eg)
HappyRestControlerTest.java
#Test
void testUnHappy() {
...
assertThrows(UnhappyException.class () -> {happyService.getById(-1L)});
}
Is this unnecessary test since I tested the behaviour of happyService in HappyServiceTest?
Considering your example is a unit test, the real HappyService should not even be in this equation and should be mocked. So if you were writing a test for the controller facade, you'd essentially be testing that it
A. Calls HappyService, given ID X, Y is returned
B. Calls HappyService, given ID X, exception is thrown and propagated.
However a unit test is quite pointless for controllers whose only job is to forward a call to a service. There is a better way to test controllers where a web server is brought up and the test is performed against your Spring context to the extent that you choose, allowing you to test the controller by making HTTP requests to it and checking its output that way. This approach is outlined in Testing the Web Layer.
For this layer of application there is a specific type of testing. It is called MVC testing and for this you mock your service with some specific response for some specific input and you verify with a test that the Controller behaves as expected.
See the following example
#SpringBootTest
#AutoConfigureMockMvc
public class TestingWebApplicationTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private HappyService service;
#Test
public void shouldReturnMessage() throws Exception {
when(service.getById(1)).thenReturn("Happy Response 1");
this.mockMvc.perform(get("/happy/1"))
.andExpect(status().isOk())
.andExpect(content()
.string(containsString("Happy Response 1")));
}
}
This is a type of test that simulates, what the client will receive from controller http status code, http headers, http body content etc...
Spring Boot already includes support for this MockMvc testing via the dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
There are different types of testing. You are talking about unit testing. In Unit testing you need to strive to break the code to the smallest logical blocks (units) and test them each with a separate unit test. As you mentioned, you already tested internal logic of your controller elsewhere. And that is correct. Testing a controller itself in unit testing doesn't make much sense at all. So, the answer to your question - no controller itself should not be tested in Unit Testing. There other types of tests such as load (stress) test, Functional tests etc. Controller should be tested in Functional testing where you test the entire logical path rather then the smallest logical unit. I won't get into details how it is done here, but controller basic functionality test is part of functional test and not unit test.

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

Unit testing a REST-Service

I have a REST-Service based on the spark-framework. Looks like this(simplyfied):
public void init() {
get(new Route("spark/favorites") {
#Override
public Object handle(Request request, Response response) {
ExternalService exS= new ExternalService();
ArrayList<String> favs= exS.getFavorites();
Gson gson = getGson();
return gson.toJson(favs);
}
});
}
Now I want to write some tests for my service to see if my get/post/put/delete does what I want.
Therefor I deploy it on an embedded Jetty during my tests.
The problem I a facing now is that my service depends on external REST-Services. I would like to mock all calls to those (to have a fast unit test). But I have no idea how to mock inside the running service.
Is that even possible? Should I switch to another REST-Framework?
Suggestions?
that's an integration test, no matter if your app talks to webservice mocks or real 3rd party webservices. Unit test are when you test your classes in isolation.
If you want to mock out external webservices you would need to make links to 3rd party apps configurable and have a separate configuration just for integration testing.
For webservice mocking you could use one of several available mocking frameworks:
https://sourceforge.net/projects/soaprest-mocker

Spring MVC: Testing particular annotated method gets invoked

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.)

Categories

Resources