I'm getting started with micronaut and I would like to understand the difference between testing the controller using local host and using an Embedded server
For example I have a simple controller
@Controller("/hello")
public class HelloController {
@Get("/test")
@Produces(MediaType.TEXT_PLAIN)
public String index() {
return "Hello World";
}
}
and the tested class
@MicronautTest
public class HelloControllerTest {
@Inject
@Client("/hello")
RxHttpClient helloClient;
@Test
public void testHello() {
HttpRequest<String> request = HttpRequest.GET("/test");
String body = helloClient.toBlocking().retrieve(request);
assertNotNull(body);
assertEquals("Hello World", body);
}
}
I got the logs:
14:32:54.382 [nioEventLoopGroup-1-3] DEBUG mylogger - Sending HTTP Request: GET /hello/test
14:32:54.382 [nioEventLoopGroup-1-3] DEBUG mylogger - Chosen Server: localhost(51995)
But then, in which cases we need an Embedded Server? why? where I can find documentation to understand it. I read the documentation from Micronaut but is not clear for me, what is actually occurring and why? like this example:
@Test
public void testIndex() throws Exception {
EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class);
RxHttpClient client = server.getApplicationContext().createBean(RxHttpClient.class, server.getURL());
assertEquals(HttpStatus.OK, client.toBlocking().exchange("/hello/status").status());
server.stop();
}
In both cases, you are using
EmbeddedServer
implementation -NettyHttpServer
. This is an abstraction that represents Micronaut server implementation (aNettyHttpServer
in this case).The main difference is that
micronaut-test
provides components and annotations that make writing Micronaut HTTP unit tests much simpler. Beforemicronaut-test
, you had to start up your application manually with:Then you had to prepare an HTTP client, for instance:
The
micronaut-test
simplifies it to adding@MicronautTest
annotation over the test class, and the runner starts the embedded server and initializes all beans you can inject. Just like you do with injectingRxHttpClient
in your example.The second thing worth mentioning is that the
@MicronautTest
annotation also allows you to use@MockBean
annotation to override existing bean with some mock you can define at the test level. By default,@MicronautTest
does not mock any beans, so the application that starts reflect 1:1 application's runtime environment. The same thing happens when you startEmbeddedServer
manually - this is just a programmatic way of starting a regular Micronaut application.So the conclusion is quite simple - if you want to write less boilerplate code in your test classes, use
micronaut-test
with all its annotations to make your tests simpler. Without it, you will have to manually control all things (starting Micronaut application, retrieving beans from application context instead of using@Inject
annotation, and so on.)Last but not least, here is the same test written without
micronaut-test
:In this case, we can't use
@Inject
annotation and the only way to create/inject beans is to useapplicationContext
object directly. (Keep in mind that in this case,RxHttpClient
bean does not exist in the context and we have to create it - inmicronaut-test
case this bean is prepared for us upfront.)And here is the same test that uses
micronaut-test
to make the test much simpler:Less boilerplate code, and the same effect. We could even
@Inject EmbeddedServer embeddedServer
if would like to access it, but there is no need to do so.