经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
SpringBoot单元测试使用@Test没有run方法的解决方案
来源:jb51  时间:2022/1/17 18:38:52  对本文有异议

SpringBoot单元测试使用@Test没有run方法

吐了!一个关键字,纠错两小时,看了十几篇博客。。。。最后重新建测试类发现@Test又有用,结果发现是因为默认的Tests测试类没有public关键字!

在这里插入图片描述

在这里插入图片描述

这个破错改了两小时。。。

==后续来了:==

原因找到了

建项目的时候是默认的2.3.0,所以默认创建的类结构应该是2.3.0版本的。

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.3.0.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>

emmm。之前因为改成了2.1.7.RELEASE,版本不同,项目的结构也不同。

现在用回2.3.0RELEASE

是可以正常跑的。。。而且也没有了@RunWith注解

在这里插入图片描述

SpringBoot写单元测试遇到的坑

近期,项目需要写单元测试。我着手的项目是用SpringBoot写的。所以就简单的研究了一下如何使用。在使用中遇到不少问题,不得已换了一种方式写测试用例,写完之后总感觉不太爽。今天在Spring官网上学一个新的用法,发现这种测试方法使用后没有问题。所以来写一点笔记。

SpringBoot怎么写单元测试

SpringBoot提供注解的方式编写单元测试,可以使用SpringBootTest注解来标示测试类。

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class SpringBootTest{
  4. @Test
  5. public void method(){
  6. }
  7. }

这样写只能解决没有一些配置文件的测试逻辑,比如没有数据库配置、数据库连接池配置等。如果有这些配置,你就需要这样写了。

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. @Test
  4. public void method(){
  5. }

这样就可以正常运行了。

测试controller类。使用了Mock,网上大多流传的是下面这种方法,添加@WebAppConfiguration,使用MockMvc去进行单元测试,但是我的项目如下使用就出现了问题,执行的时候找不到Controller类,网上百度了各种方法都不管用。都会报 no bean of 'controller' type found错误。

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. @WebAppConfiguration
  4. public class ControllerTest {
  5. private MockMvc mockMvc;
  6. @Autowired
  7. private WebApplicationContext wac;
  8. @Before // 在测试开始前初始化工作
  9. public void setup() {
  10. this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
  11. }
  12. @Test
  13. public void getMessageTest() throws Exception {
  14. MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/test/getMessage"))
  15. .andDo(MockMvcResultHandlers.print()).andReturn();
  16. int status = mvcResult.getResponse().getStatus();
  17. String content = mvcResult.getResponse().getContentAsString();
  18. Assert.assertTrue("success", status == 200);
  19. Assert.assertFalse("failed", status != 200);
  20. System.out.println("content" + content);
  21. }

后来换了一种方式直接new个controller。测试运行后不报no bean of 'controller' type found错误了,但是在controller中使用的service报了空指针异常NPE,传递性就很明显了,controller是new的一个对象,所以注解不起作用,service就为null。

最后通过使用@AutoConfigureMockMvc+@MockBean的方式可以实现简单的单元测试,并且不会对数据产生影响,且不会对数据库产生影响。

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = Application.class)
  3. @AutoConfigureMockMvc
  4. public class ImkfMessageReportControllerTest {
  5. /**
  6. * 初始化MockMvc
  7. */
  8. @Autowired
  9. private MockMvc mvc;
  10. /**
  11. * 测试的controller
  12. */
  13. @MockBean
  14. private UserController userController;
  15. @Test
  16. public void getUserListTest() throws Exception {
  17. MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get("/user/getUserList"))
  18. .andExpect(MockMvcResultMatchers.status().isOk())
  19. .andDo(MockMvcResultHandlers.print())
  20. .andReturn();
  21. String content = mvcResult.getResponse().getContentAsString();
  22. System.out.println("content" + content);
  23. }

SpringBoot使用Mockito进行单元测试

上面是使用MockMvc,虽然能够验证短链接甚至service代码逻辑的正确性,能够正常测试接口的问题。但是缺点也不少,比如,覆盖率并没有提升。Mockito是一个非常好用的单元测试工具,它的实现原理是继承要Mock的类,将所有的公有方法进行重写

  1. @RunWith(MockitoJUnitRunner.class)
  2. public class UserServiceTest {
  3. @Mock
  4. private UserMapper userMapper;
  5. @InjectMocks
  6. private UserService userService;
  7. @Test
  8. public void saveTest() throws Exception {
  9. User user = new User();
  10. user.setUserName(Long.valueOf("springBoot"));
  11. when(userMapper.insert(user)).thenReturn(user);
  12. int num = userService.save(user);
  13. Assert.assertEquals("success", 1, 1);
  14. }
  15. }

使用RunwWith(MockitoJUnitRunner.class)(也可以使用SpringBootRunner.class)来进行mocktio测试,注解@Mock标记一个类或者接口是需要被mock的,在Mock的过程中就相当于@Resource,但是注意一点是Mock是继承or实现了Mock的类,所以Mock出来的方法,全是null或者返回值为null。@InjectMocks将所有的mock对象都放入需要测试的类的对象中。在上面的saveTest方法里面调用到UserMapper.insert(),那么需要对UserMapper.insert()进行打桩,设置预期返回值。

打桩的时候需要注意:传递的参数(如果有)必须为调用时的同一个对象或者相同值,如果传入的参数是一个对象,那么需要对这个对象进行打桩,再打桩这个方法。比如,when(userMapper.insert(user)).thenReturn(rUser),插入一个user对象,如果user插入之前要进行校验或者其他操作,需要对这个对象进行打桩(当然pojp对象可以直接new)。

如果插入的对象非常复杂,用构造方法来构造一个空对象,或者构造方法所用的对象不能直接构造,但是没有public的方法来设置值,该如何解决这个问题?我们知道一个对象的类可能有get方法(不一定是get,但是只要我们想获取这个对象中的参数,那么就有public的方法获取),我们可以通过Mock这个对象,在将要测试的方法体内,如果某行调用了这个对象的任意方法(toString()、equals()、get()),我们都可以以相同的参数(如果遇到参数未知可以用any(),一般都能知道)进行打桩后设置返回值,这样就能通过参数校验等环节,执行后面的代码逻辑,同时能够提高覆盖率,伪代码如下。

  1. @Mock
  2. private User user;
  3. when(user.get(eq("userName"))).thenReturn("testAdmin");
  4. when(user.get(eq("seq"))).thenReturn(4);
  5. when(user.get(eq("password"))).thenReturn("123456");
  6. when(user.get(eq("u_id"))).thenReturn("654321");

通过真实测试用例测试代码

Mockito测试需要设置参数和预期返回值,在方法体中遇到的所有未知对象(除了方法体中new的对象不需要)都需要进行模拟,但是在SpringBoot代码刚刚完成的初期时,跟想模拟真实场景下进行单元测试代码问题or配置问题,那么通过自动注入的方式引入对象是一种更好的选择。

  1. ProviderServiceImpl.java -----服务类
  2. import com.alibaba.dubbo.config.annotation.Service;
  3. import com.example.demo.service.DemoService;
  4. @Service
  5. public class ProviderServiceImpl implements DemoService {
  6. @Override
  7. public String sayHello(String name) {
  8. return "hello " + name + " !";
  9. }
  10. }
  11. DemoApplicationTests.java -----测试类
  12. @RunWith(SpringRunner.class)
  13. @SpringBootTest
  14. public class DemoApplicationTests {
  15. @Resource
  16. DemoService providerService;
  17. @Test
  18. public void contextLoads() {
  19. String result = providerService.sayHello("Spring Boot Test");
  20. System.out.println("result is "+result);
  21. Assert.assertEquals("success","hello Spring Boot Test !" ,result);
  22. }
  23. }

这里需要注意的是DemoApplicationTests 需要跟启动类main在同一级目录下,如果跟mvc在同一层可以会出现部分bean扫描不到的情况。如目录层级很深或者程序启动比较慢的话,可以去掉SpringBootTest(去掉后就不启动程序,只会运行该测试),运行一下,测试结果如下:

通过这种注解的方式,可以测试dubbo连接(Refernce注解),可以测试controller层,redis数据,mysql数据,都会真实模拟,你只需要在注入你需要测试的类,在类的入口传入测试参数,在测试过程中,最好采用debug的方式,这样你可以看到每一步的数据,也便于定位程序的问题(当然也可以出现问题时使用debug)。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号