之前的编写代码的时候,由于时间限制等原因,一直觉得写单元测试是额外的工作量,也不爱写或者是开发完成之后再后补,有应付了事的嫌疑,没有做出单元测试的真是意义。但是这样往往会在修改完一个问题之后,可能引起其他问题而不自知。
所以单元测试还是有必要的。然后最近就研究了一下,应该用何种方式,什么工具去做测试,网上也找了一堆资料,基本上都是spock(groovy编写,还未使用过,后续试试),还有说要借鉴appfuse的测试思想去做(自行百度吧),什么DBUnit ,JUnit等等吧。
这些只是自己初略了解了下,但是具体有些也清楚怎么用,鉴于自己的需求,最后选择了JUnit,mockMvc,h2 database
自身系统情况:
使用Springboot + mybatis + mySql 实现的web项目
大致的思路是把三层都可以进行单元测试
1、dao
2、service
3、controller
1、dao
SpringBoot 整合H2测试Dao可以查看这篇日志: http://blog.csdn.net/mn960mn/article/details/54644908
https://segmentfault.com/a/1190000007002140
因为 h2的配置中初始话表数据的时候,如果直接是使用INIT=RUNSCRIPT FROM 这种引入插入数据的时候,但是不知道为什么 数据被插入了10遍,所以没办法只能更换方式,使用spring.datasource.data 进行初始化了。
## h2 database spring.datasource.driver-class-name=org.h2.Driver spring.datasource.url=jdbc:h2:mem:test;MODE=MYSQL; ##INIT=RUNSCRIPT FROM 'classpath:sql/init_table.sql'\\;RUNSCRIPT FROM 'classpath:sql/init_data.sql' ## this i don't konw why it insert 10 datas #spring.datasource.url=jdbc:h2:file:~/.h2/db_users;MODE=MYSQL;AUTO_SERVER=TRUE; ## file save to local spring.datasource.username= spring.datasource.password= spring.datasource.schema=classpath:sql/init_table.sql spring.datasource.data=classpath:sql/init_data.sql
2、service
测试service也比较简单,只需要执行service的扫描包就可以了。这样在测试类中就可以使用相应的bean了。
@Configuration @ComponentScan(basePackages = {"com.test.service.**"}) public class ServiceConfiguration { }
3、controller
这个是在测试的时候问题比较多的地方,其实原本如果使用的是正常的mysql数据库(跟平时开发用的一个)也还好,但是为了实现单体测试和开发那个数据库分离,添加的这个H2数据库就出现问题了。
原因是我们自己重新定义了datasource
@Configuration @MapperScan({ "com.xxx.**.mapper"}) @ComponentScan({ "com.xxx.datasource"}) @Slf4j public class MapperAdminConfiguration { @Bean @ConfigurationProperties(prefix="my.jdbc") public MyDataSource dataSource() { return (MyDataSource ) DataSourceBuilder.create() .type(MyDataSource.class).build(); } }
注意: 以上代码有一些自身的逻辑,所以直接修改配置变为h2应该是不好用的,因为是后加的Test,所以不会动原来的代码
正常在Test文件中,指定 @SpringBootTest(classes={Application.class})就可以把需要的内容都进行加载初始化,但是由于通过@bean形式配置了datasource,那么加载时就会把配置文件中配置的默认的datasource进行覆盖,而去使用my.jdbc开头的配置信息。就没办法使用H2数据库了.
那么就想到了直接使用@SpringBootTest(classes={Application.class})这个。而是只引入自己需要的,就像Dao测试那样,然后自己就建立了一个
@Configuration @ComponentScan(basePackages = {"com.xxxx.**") public class ServiceConfiguration { }
@RunWith(SpringRunner.class) /** * 这里指定的classes是可选的。如果不指定classes,则spring boot会启动整个spring容器,很慢(比如说会执行一些初始化,ApplicationRunner、CommandLineRunner等等)。不推荐 * 指定的话,就只会初始化指定的bean,速度快,推荐 */ @SpringBootTest(classes={DataSourceAutoConfiguration.class, MybatisAutoConfiguration.class, MapperConfiguration.class,ServiceConfiguration.class}) @WebAppConfiguration public class PlatformControllerTest { MockMvc mvc; @Autowired WebApplicationContext webApplicationContext; @Before public void setUp() { mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test public void list() throws Exception { String uri = "/admin/admin/platform/list/all"; MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(uri).accept(MediaType.APPLICATION_JSON)).andReturn(); int status = mvcResult.getResponse().getStatus(); String result = mvcResult.getResponse().getContentAsString(); Assert.assertTrue("Wrong,status not 200 ,is " + status, status == 200); Assert.assertFalse("Wrong,status not 200 ,is " + status, status != 200); } //@Test public void testList(){ RestTemplate restTemplate = new RestTemplate();//测试http请求的 String url = "http://localhost:8080/admin/admin/platform/list/all"; Map map = new HashMap<>(); String result = restTemplate.getForObject(url, String.class, map); System.out.print(result+"========="); } }
但是问题出现了,因为返回的是对象,在list()方法返回的http状态一直是406,原因应该是在messageConverter的时候没有转,正常情况下SpringBoot是有一个WebMvcAutoConfiguration ,里边会添加上一些默认的HttpMessageConverters,自动进行转换的,但是现在不好用了,说明这个没有进行加载。
为什么使用 @SpringBootTest(classes={Application.class}) 可以加载进来,具体是怎么加载我也看不明白,无奈只能先看看Application类都有哪些东西吧
@SpringBootApplication @EnableAspectJAutoProxy public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }