Phil as a nice post about the improvements on testing in Spring Boot 1.4, check it out here: Testing improvements in Spring Boot 1.4.
I’d like to add a concrete example for those and some more, please have a look at the comments inside one of the most important controllers I’ve ever written 😉
Those are the dependencies you’ll need for the demo:
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.springframework.restdocs</groupid>
<artifactid>spring-restdocs-mockmvc</artifactid>
<scope>test</scope>
</dependency>
</dependencies> |
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.springframework.restdocs</groupid>
<artifactid>spring-restdocs-mockmvc</artifactid>
<scope>test</scope>
</dependency>
</dependencies>
Here’s the complete demo application:
import java.io.IOException;
import java.io.PrintStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@SpringBootApplication
public class Demo14Application {
@Controller
@RequestMapping("/api/banner")
static class BannerController {
// 1) If you didn't chose to disable banner in your application (and why
// should you? ;) ), the banner will be availabe as a bean in your
// application
// 2) banner.jpg :) You can replace banner.txt with a banner.jpg or banner.png
// to generate some nice ascii art during startup
private final Banner banner;
private final Environment environment;
// 3) no more @Autowired on constructors necessary
// when there's on a unique non-default constructor
// This also works on @Configuration classes, which didn't support
// constructor injection at all so far.
// This comes actually from Spring Framework 4.3.RC1
public BannerController(final Banner banner, final Environment environment) {
this.banner = banner;
this.environment = environment;
}
// 4) More annotations ;)
// Precomposed @GetMapping, @PostMapping, @RequestScope, @SessionScope etc
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public void get(final HttpServletResponse response) throws IOException {
try (PrintStream printStream = new PrintStream(response.getOutputStream())) {
banner.printBanner(environment, BannerController.class, printStream);
}
}
}
public static void main(String[] args) {
SpringApplication.run(Demo14Application.class, args);
}
} |
import java.io.IOException;
import java.io.PrintStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@SpringBootApplication
public class Demo14Application {
@Controller
@RequestMapping("/api/banner")
static class BannerController {
// 1) If you didn't chose to disable banner in your application (and why
// should you? ;) ), the banner will be availabe as a bean in your
// application
// 2) banner.jpg :) You can replace banner.txt with a banner.jpg or banner.png
// to generate some nice ascii art during startup
private final Banner banner;
private final Environment environment;
// 3) no more @Autowired on constructors necessary
// when there's on a unique non-default constructor
// This also works on @Configuration classes, which didn't support
// constructor injection at all so far.
// This comes actually from Spring Framework 4.3.RC1
public BannerController(final Banner banner, final Environment environment) {
this.banner = banner;
this.environment = environment;
}
// 4) More annotations ;)
// Precomposed @GetMapping, @PostMapping, @RequestScope, @SessionScope etc
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public void get(final HttpServletResponse response) throws IOException {
try (PrintStream printStream = new PrintStream(response.getOutputStream())) {
banner.printBanner(environment, BannerController.class, printStream);
}
}
}
public static void main(String[] args) {
SpringApplication.run(Demo14Application.class, args);
}
}
And here’s the test. I really like the simplifications there:
import BannerController;
import java.io.PrintStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// 5) Easier named JUnit Runner ;)
@RunWith(SpringRunner.class)
// 6) Test slices of your application, in this case the Spring MVC slice
// similiar options are available for @DataJpaTest and @JsonTest (incuding
// JacksonTester).
@WebMvcTest(controllers = BannerController.class)
// 7) Together with the above auto configuration comes autoconfiguration of
// Spring REST docs, which removes the need for the JUnit rule and additional
// configuration of the mock mvc instance
// BTW: It seams that you can use addtitional formats like markdown for the
// snippets
@AutoConfigureRestDocs(
outputDir = "target/generated-snippets",
uriHost = "banner-as-a-service.io",
uriPort = 80
)
public class Demo14ApplicationTests {
// 8) Spring Boot includes a @MockBean annotation that can be used to define
// a Mockito mock for a bean inside your ApplicationContext. You can use
// the annotation to add new beans, or replace a single existing bean
// definition.
@MockBean
private Banner banner;
@Autowired
private MockMvc mockMvc;
@Test
public void testSomeMethod() throws Exception {
doAnswer(invocation -> {
final PrintStream out = invocation.getArgumentAt(2, PrintStream.class);
out.write(bannerText.getBytes());
return null;
}).when(banner).printBanner(anyObject(), anyObject(), anyObject());
mockMvc
.perform(
get("/api/banner").accept(APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().string(bannerText))
.andDo(document("api/banner",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
}
private final String bannerText
= " _____ _ ______ _____ _____ _____ _ \n"
+ "/ ___| (_) | ___ \\ ___/ ___|_ _| | | \n"
+ "\\ `--. _ __ _ __ _ _ __ __ _ | |_/ / |__ \\ `--. | | __| | ___ ___ ___ \n"
+ " `--. \\ '_ \\| '__| | '_ \\ / _` | | /| __| `--. \\ | | / _` |/ _ \\ / __/ __|\n"
+ "/\\__/ / |_) | | | | | | | (_| | | |\\ \\| |___/\\__/ / | | | (_| | (_) | (__\\__ \\\n"
+ "\\____/| .__/|_| |_|_| |_|\\__, | \\_| \\_\\____/\\____/ \\_/ \\__,_|\\___/ \\___|___/\n"
+ " | | __/ | \n"
+ " |_| |___/ \n"
+ " _ _ _ ___ _____ _____ _____ _____ _ \n"
+ " (_) | | | / _ \\ / ___/ __ \\_ _|_ _| | | \n"
+ "__ ___| |_| |__ / /_\\ \\\\ `--.| / \\/ | | | | __ _ _ __| |_ \n"
+ "\\ \\ /\\ / / | __| '_ \\ | _ | `--. \\ | | | | | / _` | '__| __| \n"
+ " \\ V V /| | |_| | | | | | | |/\\__/ / \\__/\\_| |_ _| |_ | (_| | | | |_ \n"
+ " \\_/\\_/ |_|\\__|_| |_| \\_| |_/\\____/ \\____/\\___/ \\___/ \\__,_|_| \\__| \n"
+ " \n"
+ " ";
} |
import BannerController;
import java.io.PrintStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.Banner;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// 5) Easier named JUnit Runner ;)
@RunWith(SpringRunner.class)
// 6) Test slices of your application, in this case the Spring MVC slice
// similiar options are available for @DataJpaTest and @JsonTest (incuding
// JacksonTester).
@WebMvcTest(controllers = BannerController.class)
// 7) Together with the above auto configuration comes autoconfiguration of
// Spring REST docs, which removes the need for the JUnit rule and additional
// configuration of the mock mvc instance
// BTW: It seams that you can use addtitional formats like markdown for the
// snippets
@AutoConfigureRestDocs(
outputDir = "target/generated-snippets",
uriHost = "banner-as-a-service.io",
uriPort = 80
)
public class Demo14ApplicationTests {
// 8) Spring Boot includes a @MockBean annotation that can be used to define
// a Mockito mock for a bean inside your ApplicationContext. You can use
// the annotation to add new beans, or replace a single existing bean
// definition.
@MockBean
private Banner banner;
@Autowired
private MockMvc mockMvc;
@Test
public void testSomeMethod() throws Exception {
doAnswer(invocation -> {
final PrintStream out = invocation.getArgumentAt(2, PrintStream.class);
out.write(bannerText.getBytes());
return null;
}).when(banner).printBanner(anyObject(), anyObject(), anyObject());
mockMvc
.perform(
get("/api/banner").accept(APPLICATION_JSON)
)
.andExpect(status().isOk())
.andExpect(content().string(bannerText))
.andDo(document("api/banner",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())
));
}
private final String bannerText
= " _____ _ ______ _____ _____ _____ _ \n"
+ "/ ___| (_) | ___ \\ ___/ ___|_ _| | | \n"
+ "\\ `--. _ __ _ __ _ _ __ __ _ | |_/ / |__ \\ `--. | | __| | ___ ___ ___ \n"
+ " `--. \\ '_ \\| '__| | '_ \\ / _` | | /| __| `--. \\ | | / _` |/ _ \\ / __/ __|\n"
+ "/\\__/ / |_) | | | | | | | (_| | | |\\ \\| |___/\\__/ / | | | (_| | (_) | (__\\__ \\\n"
+ "\\____/| .__/|_| |_|_| |_|\\__, | \\_| \\_\\____/\\____/ \\_/ \\__,_|\\___/ \\___|___/\n"
+ " | | __/ | \n"
+ " |_| |___/ \n"
+ " _ _ _ ___ _____ _____ _____ _____ _ \n"
+ " (_) | | | / _ \\ / ___/ __ \\_ _|_ _| | | \n"
+ "__ ___| |_| |__ / /_\\ \\\\ `--.| / \\/ | | | | __ _ _ __| |_ \n"
+ "\\ \\ /\\ / / | __| '_ \\ | _ | `--. \\ | | | | | / _` | '__| __| \n"
+ " \\ V V /| | |_| | | | | | | |/\\__/ / \\__/\\_| |_ _| |_ | (_| | | | |_ \n"
+ " \\_/\\_/ |_|\\__|_| |_| \\_| |_/\\____/ \\____/\\___/ \\___/ \\__,_|_| \\__| \n"
+ " \n"
+ " ";
}
Have a look at the release notes or the updated reference documentation for more.
Read the complete article »
Filed in English posts
|