diff --git a/README.adoc b/README.adoc index c9f9b920d9a424e57f940077e23288d04330a529..c6f4b1b88e2b146bfd9636415f24f0b754497a88 100644 --- a/README.adoc +++ b/README.adoc @@ -136,6 +136,44 @@ $ curl localhost:8080 Greetings from Spring Boot! .... +== Add Unit Tests + +You will want to add a test for the endpoint you added, and Spring Test already provides some machinery for that, and it's easy to include in your project. + +Add this to your build file's list of dependencies: + +[source,groovy] +---- +include::complete/build.gradle[tag=tests] +---- + +If you are using Maven, add this to your list of dependencies: + +[source,xml] +---- +include::complete/pom.xml[tag=tests] +---- + +Now write a simple unit test that mocks the servlet request and response through your endpoint: + +`src/test/java/hello/HelloControllerTest.java` +[source,java] +---- +include::initial/src/test/java/hello/HelloControllerTest.java[] +---- + +Note the use of the `MockServletContext` to set up an empty `WebApplicationContext` so the `HelloController` can be created in the `@Before` and passed to `MockMvcBuilders.standaloneSetup()`. An alternative would be to create the full application context using the `Application` class and `@Autowired` the `HelloController` into the test. The `MockMvc` comes from Spring Test and allows you, via a set of convenient builder classes, to send HTTP requests into the `DispatcherServlet` and make assertions about the result. + +As well as mocking the HTTP request cycle we can also use Spring Boot to write a very simple full-stack integration test. For example, instead of (or as well as) the mock test above we could do this: + +`src/test/java/hello/HelloControllerTest.java` +[source,java] +---- +include::initial/src/test/java/hello/HelloControllerIT.java[] +---- + +The embedded server is started up on a random port by virtue of the `@IntegrationTest("${server.port=0}")` and the actual port is discovered at runtime with the `@Value("${local.server.port}")`. + == Add production-grade services If you are building a web site for your business, you probably need to add some management services. Spring Boot provides several out of the box with its http://docs.spring.io/spring-boot/docs/{spring_boot_version}/reference/htmlsingle/#production-ready[actuator module], such as health, audits, beans, and more. diff --git a/complete/build.gradle b/complete/build.gradle index 446ec000265a94d70342f5e4978b5af45017ccae..d101345e85894dabc5bbfeb59c85b517e5219545 100644 --- a/complete/build.gradle +++ b/complete/build.gradle @@ -26,7 +26,9 @@ dependencies { // tag::actuator[] compile("org.springframework.boot:spring-boot-starter-actuator") // end::actuator[] + // tag::tests[] testCompile("org.springframework.boot:spring-boot-starter-test") + // end::tests[] } task wrapper(type: Wrapper) { diff --git a/complete/pom.xml b/complete/pom.xml index eb8b8ee84490907340e8f978489145f2557ec0ac..801e46e01c7af7ee0655a3280a7795f199674a61 100644 --- a/complete/pom.xml +++ b/complete/pom.xml @@ -24,11 +24,13 @@ spring-boot-starter-actuator + org.springframework.boot spring-boot-starter-test test + diff --git a/complete/src/main/java/hello/HelloController.java b/complete/src/main/java/hello/HelloController.java index fd7ed0a5d0adcb6359af9177cc8811e82f446a59..d9c1069cc5974931d491d6502ccb1d258663ad1c 100644 --- a/complete/src/main/java/hello/HelloController.java +++ b/complete/src/main/java/hello/HelloController.java @@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.RequestMapping; @RestController public class HelloController { - @RequestMapping("/hello") + @RequestMapping("/") public String index() { return "Greetings from Spring Boot!"; } diff --git a/complete/src/test/java/hello/HelloControllerIT.java b/complete/src/test/java/hello/HelloControllerIT.java index 172896f9bf5f02c2f965f734921ea941c5a64831..ba1d64475447d8d6ad3139e779bf622d898af532 100644 --- a/complete/src/test/java/hello/HelloControllerIT.java +++ b/complete/src/test/java/hello/HelloControllerIT.java @@ -1,8 +1,14 @@ package hello; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +import java.net.URL; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.TestRestTemplate; @@ -11,22 +17,21 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.web.client.RestTemplate; -import java.net.URL; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration -@IntegrationTest({"server.port=9090"}) +@IntegrationTest({"server.port=0"}) public class HelloControllerIT { + + @Value("${local.server.port}") + private int port; + private URL base; private RestTemplate template; @Before public void setUp() throws Exception { - this.base = new URL("http://localhost:9090/hello"); + this.base = new URL("http://localhost:" + port + "/"); template = new TestRestTemplate(); } diff --git a/complete/src/test/java/hello/HelloControllerTest.java b/complete/src/test/java/hello/HelloControllerTest.java index e0b450e39b8b22574aa6655f51a7b254ff30ad77..81fecad69cd3176c6ff8e3b1201cb0fb857cdd69 100644 --- a/complete/src/test/java/hello/HelloControllerTest.java +++ b/complete/src/test/java/hello/HelloControllerTest.java @@ -1,27 +1,26 @@ package hello; +import static org.hamcrest.Matchers.is; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.http.MediaType; import org.springframework.mock.web.MockServletContext; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import static org.hamcrest.Matchers.is; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - @RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(classes = Application.class) -@ContextConfiguration(classes = MockServletContext.class) +@SpringApplicationConfiguration(classes = MockServletContext.class) @WebAppConfiguration public class HelloControllerTest { + private MockMvc mvc; @Before @@ -31,7 +30,7 @@ public class HelloControllerTest { @Test public void getHello() throws Exception { - mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) + mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string(is("Greetings from Spring Boot!"))); } diff --git a/initial/pom.xml b/initial/pom.xml index 742a6309bf7b817957f7fd89126908866d30189a..96f104f1750eb8b5c60e60e957ab23ab1ef27aed 100644 --- a/initial/pom.xml +++ b/initial/pom.xml @@ -14,28 +14,10 @@ - org.springframework.boot spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - - - - org.springframework.boot - spring-boot-starter-jetty - - - - - org.springframework.boot - spring-boot-starter-actuator - -