前言
mac上idea快捷鍵,command+shift+T根據(jù)類生成快捷鍵。
對spring容器中的類做單元測試
在src/main下建立UserService類,對其進(jìn)行單于測試,生產(chǎn)其單元測試類(使用command+shift+T快捷鍵),生成的test類在src/test下
@Service
public class UserService {
public Integer addUser(String username){
System.out.println("user dao adduser [username="+username+"]");
if(username == null){
return 0;
}
return 1;
}
}
springboot啟動(dòng)類:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
測試類:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void addUser() throws Exception {
Assert.assertEquals(Integer.valueOf(1),userService.addUser("zhihao.miao"));
Assert.assertEquals(Integer.valueOf(0),userService.addUser(null));
}
}
盲點(diǎn)掃描
RunWith注解,SpringRunner類,SpringJUnit4ClassRunner類,SpringBootTest注解的解釋。
When a class is annotated with @RunWith or extends a class annotated with @RunWith, JUnit will invoke the class it references to run the tests in that class instead of the runner built into JUnit. We added this feature late in development. While it seems powerful we expect the runner API to change as we learn how people really use it. Some of the classes that are currently internal will likely be refined and become public.
當(dāng)一個(gè)類用@RunWith注釋或繼承一個(gè)用@RunWith注釋的類時(shí),JUnit將調(diào)用它所引用的類來運(yùn)行該類中的測試而不是開發(fā)者去在junit內(nèi)部去構(gòu)建它。我們在開發(fā)過程中使用這個(gè)特性。
For example, suites in JUnit 4 are built using RunWith, and a custom runner named Suite:
比如說,suites使用RunWith注解構(gòu)建,
@RunWith(Suite.class)
@SuiteClasses({ATest.class, BTest.class, CTest.class})
public class ABCSuite {
}
SpringRunner is an alias for the SpringJUnit4ClassRunner.
SpringRunner
是SpringJUnit4ClassRunner
的一個(gè)別名。
To use this class, simply annotate a JUnit 4 based test class with @RunWith(SpringRunner.class).
使用這個(gè)類,簡單注解一個(gè)JUnit 4 依賴的測試@RunWith(SpringRunner.class).
If you would like to use the Spring TestContext Framework with a runner other than
this one, use org.springframework.test.context.junit4.rules.SpringClassRule
and org.springframework.test.context.junit4.rules.SpringMethodRule.
如果你想使用Spring測試上下文而不是使用這個(gè),你可以使用org.springframework.test.context.junit4.rules.SpringClassRule
和org.springframework.test.context.junit4.rules.SpringMethodRule
.
SpringJUnit4ClassRunner is a custom extension of JUnit's
BlockJUnit4ClassRunner which provides functionality of the
Spring TestContext Framework to standard JUnit tests by means of the
TestContextManager and associated support classes and annotations.
SpringJUnit4ClassRunner是JUnit's的BlockJUnit4ClassRunner類的一個(gè)常規(guī)擴(kuò)展,提供了一些spring測試環(huán)境上下文去規(guī)范JUnit測試,意味著TestContextManager和支持相關(guān)的類和注解。
Annotation that can be specified on a test class that runs Spring Boot based tests.
Provides the following features over and above the regular Spring TestContext
Framework:
注解制定了一個(gè)測試類運(yùn)行了Spring Boot環(huán)境。提供了以下一些特性:
Uses SpringBootContextLoader as the default ContextLoader when no specific ContextConfiguration#loader() @ContextConfiguration(loader=...) is defined.
當(dāng)沒有特定的ContextConfiguration#loader()(@ContextConfiguration(loader=...))被定義那么就是SpringBootContextLoader作為默認(rèn)的ContextLoader。
Automatically searches for a SpringBootConfiguration @SpringBootConfiguration when nested @Configuration is not used, and no explicit #classes() classes are
specified.
自動(dòng)搜索到SpringBootConfiguration注解的文件。
Allows custom Environment properties to be defined using the properties() properties attribute}.
允許自動(dòng)注入Environment類讀取配置文件。
Provides support for different #webEnvironment() webEnvironment modes,
including the ability to start a fully running container listening on a
WebEnvironment#DEFINED_PORT defined or WebEnvironment#RANDOM_PORT
random port.
提供一個(gè)webEnvironment環(huán)境,可以完整的允許一個(gè)web環(huán)境使用隨機(jī)的端口或者自定義的端口。
Registers a org.springframework.boot.test.web.client.TestRestTemplate
TestRestTemplate bean for use in web tests that are using a fully running container.
注冊了TestRestTemplate類可以去做接口調(diào)用。
springboot測試步驟
- 直接在測試類上面加上如下2個(gè)注解
@RunWith(SpringRunner.class)
@SpringBootTest
就能取到spring中的容器的實(shí)例,如果配置了@Autowired那么就自動(dòng)將對象注入。
在測試環(huán)境中獲取一個(gè)bean,在項(xiàng)目中新建User類,然后在測試模塊進(jìn)行測試
在src/main下新建一個(gè)實(shí)例User
@Component
public class User {
}
src/test下創(chuàng)建測試類測試:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
public ApplicationContext context;
@Test
public void testNotNull(){
Assert.assertNotNull(context.getBean(User.class));
}
}
只在測試環(huán)境有效的bean
在src/test下新建二個(gè)類,我們發(fā)現(xiàn)分別使用@TestComponent和@TestConfiguration二個(gè)注解修飾,這些類只在測試環(huán)境生效
@TestComponent
public class Cat {
public void index(){
System.out.println("cat index");
}
}
@TestConfiguration
public class TestBeanConfiguration {
@Bean
public Runnable createRunnable(){
return () -> System.out.println("=====createRunnable=======");
}
}
測試類:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestBeanConfiguration.class,Cat.class})
public class TestApplication {
@Autowired
public ApplicationContext context;
@Test
public void testNotNull(){
Runnable runnable = context.getBean(Runnable.class);
runnable.run();
System.out.println("--------");
Cat cat = context.getBean(Cat.class);
cat.index();
}
}
需要在@SpringBootTest注解的參數(shù)classes中加入?yún)?shù),表示將某些類納入測試環(huán)境的容器中。
配置文件屬性的讀取
springboot會只會讀取到src/test/resources下的配置,不會讀到正式環(huán)境下的配置文件(跟以前1.4.*版本的不一樣,以前是優(yōu)先讀取測試環(huán)境配置文件,然后讀取正式環(huán)境的配置)
@RunWith(SpringRunner.class)
@SpringBootTest
public class EnvTest {
@Autowired
public Environment environment;
@Test
public void testValue(){
Assert.assertEquals("zhihao.miao",environment.getProperty("developer.name"));
}
}
除了在配置文件中設(shè)置屬性,測試環(huán)境加載一些配置信息的二種方式:
第一種是使用@SpringBootTest注解,注解參數(shù)properties指定其value值,第二種使用EnvironmentTestUtils.addEnvironment方法進(jìn)行設(shè)置。
測試:
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {"app.version=1.0"})
public class EnvTest2 {
@Autowired
private ConfigurableEnvironment environment;
@Before
public void init(){
EnvironmentTestUtils.addEnvironment(environment,"app.admin.user=zhangsan");
}
@Test
public void testApplication(){
Assert.assertEquals("1.0",environment.getProperty("app.version"));
Assert.assertEquals("zhangsan",environment.getProperty("app.admin.user"));
}
}
Mock方式的測試
正式環(huán)境只是一個(gè)接口,并沒有實(shí)現(xiàn),也并沒有納入spring容器進(jìn)行管理。
public interface UserDao {
Integer createUser(String userName);
}
測試
@RunWith(SpringRunner.class)
public class UserDaoTest {
//使用MockBean是因?yàn)榇藭r(shí)容器中沒有UserMapper這個(gè)對象
@MockBean
public UserDao userDao;
//使用BDDMockito對行為進(jìn)行預(yù)測,
@Before
public void init(){
BDDMockito.given(userDao.createUser("admin")).willReturn(1);
BDDMockito.given(userDao.createUser("")).willReturn(0);
BDDMockito.given(userDao.createUser(null)).willThrow(NullPointerException.class);
}
@Test(expected=NullPointerException.class)
public void testCreateUser() {
Assert.assertEquals(Integer.valueOf(1),userDao.createUser("admin")) ;
Assert.assertEquals(Integer.valueOf(0),userDao.createUser("")) ;
Assert.assertEquals(Integer.valueOf(1),userDao.createUser(null)) ;
}
}
對controller進(jìn)行測試
第一種方式:
定義一個(gè)Controller,用作測試:
@RestController
public class UserController {
private Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping("/user/home")
public String home(){
logger.info("user home");
return "user home";
}
@GetMapping("/user/show")
public String show(@RequestParam("id") String id){
logger.info("book show");
return "show"+id;
}
}
使用瀏覽器訪問
http://localhost:8080/user/home
http://localhost:8080/user/show?id=100
使用測試類測試
@RunWith(SpringRunner.class)
//指定web環(huán)境,隨機(jī)端口
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
//這個(gè)對象是運(yùn)行在web環(huán)境的時(shí)候加載到spring容器中
@Autowired
private TestRestTemplate testRestTemplate;
@Test
public void testHome(){
String context = testRestTemplate.getForObject("/user/home",String.class);
Assert.assertEquals("user home",context);
}
@Test
public void testShow(){
String context = testRestTemplate.getForObject("/user/show?id=100",String.class);
Assert.assertEquals("show10",context);
}
}
第二種方式,使用@WebMvcTest注解
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = UserController.class)
public class UserControllerTest2 {
@Autowired
public MockMvc mockMvc;
@Test
public void testHome() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user/home")).andExpect(MockMvcResultMatchers.status().isOk());
mockMvc.perform(MockMvcRequestBuilders.get("/user/home")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("user home"));
}
@Test
public void testShow() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/user/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk());
mockMvc.perform(MockMvcRequestBuilders.get("/user/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("show400"));
}
}
@WebMvcTest 不需要運(yùn)行在web環(huán)境下,但是,需要指定controllers,表示需要測試哪些controllers。
這種方式只測試controller,controller里面的一些依賴,需要你自己去mock
@WebMvcTest 不會加載整個(gè)spring容器。
第三種方式
使用@SpringBootTest()與@AutoConfigureMockMvc結(jié)合,@SpringBootTest使用@SpringBootTest加載測試的spring上下文環(huán)境,@AutoConfigureMockMvc自動(dòng)配置MockMvc這個(gè)類,
/**
* @SpringBootTest 不能和 @WebMvcTest 同時(shí)使用
* 如果使用MockMvc對象的話,需要另外加上@AutoConfigureMockMvc注解
*/
@RunWith(SpringRunner.class)
@SpringBootTest()
@AutoConfigureMockMvc
public class UserControllerTest3 {
@Autowired
private MockMvc mvc;
@Test
public void testHome() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/user/home")).andExpect(MockMvcResultMatchers.status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/user/home")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("user home"));
}
@Test
public void testShow() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/user/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk());
mvc.perform(MockMvcRequestBuilders.get("/user/show").param("id", "400")).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string("show400"));
}
}
一個(gè)注解可以使測試類可以自動(dòng)配置MockMvc這個(gè)類。