单元测试之简单Mock实战

这里就写一些关键代码。

service是最需要关注的地方,重要的逻辑一般都在这里;同时又有诸多的外部依赖,用这一层做实际mock的实例是最合适的。

/**
   * @author zhangpeng34
   * Created on 2019/1/18 下午9:15
  **/
  @Service
  public class UserServiceImpl implements UserService {
      @Autowired
      UserDao userDao;
      @Override
      public User findUserById(Long id) {
          return userDao.findById(id).orElse(null);
      }
      @Override
      public User addUser(String name) {
          if(StringUtils.isEmpty(name)){
              return null;
          }
          User user = new User();
          user.setName(name);
          return userDao.save(user);
      }
  }

这个service很简单,这里针对里面的addUser方法写一些对应的单测:

/**
   *
   * 单元测试,测试的目的是对java代码逻辑进行测试。
   * 单纯的逻辑测试,不应该加载外部依赖,所有的外部依赖应该mock掉,只关注本身逻辑。
   * 例如,需要测试service层时,所依赖的dao等,应提前mock掉,设置好测试需要的输入和输出即可。
   * dao层的逻辑应由dao层的测试保证,service层默认dao层是正确的。
  **/
  @RunWith(MockitoJUnitRunner.class)
  public class UserServiceTests {
      //mock注解创建一个被mock的实例
      @Mock
      UserDao userDao;
      //InjectMocks代表创建一个实例,其他带mock注解的示例将被注入到该实例用。
      //可以用该注解创建要被测试的实例,将实例所需的依赖用mock注解创建,即可mock掉依赖
      @InjectMocks
      UserServiceImpl UserServiceImpl;
      String addUserName = "testAddUser";
      /**
       * 初始化时设置一些需要mock的方法和返回值
       * 这里的设置表示碰到userDao的save方法,且参数为任一User类的实例时,返回提前预设的值
       */
      @Before
      public void init(){
          User user =new User();
          user.setId(1L);
          user.setName(addUserName);
          Mockito.when(userDao.save(any(User.class)))
                  .thenReturn(user);
      }
      //正向流程
      @Test
      public void testAddUser(){
          User user = UserServiceImpl.addUser(addUserName);
          Assert.assertEquals(addUserName,user.getName());
          Assert.assertEquals(1L,user.getId().longValue());
      }
      //异常分支,name为null
      @Test
      public void testAddUserNull(){
          User user = UserServiceImpl.addUser(null);
          Assert.assertEquals(null,user);
      }
      //将各个分支都写出test和assert
      //............
  }

集成测试

  上面所说的都是单元测试,但是实际开发中,我们往往不光需要单元测试(甚至不需要单元测试。。。。),还需要有集成测试,来测试我们程序的整体运行情况。

  集成测试并不是联调,集成测试用依然可以mock第三方依赖。

  在我们的工程里,一般只要实际启动整个spring容器的测试代码,都是集成测试。

  Controller层是较为适合集成测试的地方,这里用Controller层来做集成测试的示例。

@RestController
  public class UserController {
      @Autowired
      UserService userService;
      @PostMapping(value = "/user")
      public Object register(String name) {
          return userService.addUser(name);
      }
      @GetMapping(value = "/user/{userId}")
      public Object getUserInfo(@PathVariable Long userId) {
          User user = userService.findUserById(userId);
          if (user != null) {
              return user;
          }
          return "fail";
      }
      @PostMapping(value = "/user/login")
      public Object login(Long id, String pwd) {
          User user =  userService.findUserById(id);
          if(user!=null){
              return user;
          }
          return "fail";
      }
  }

下面写一个这次集成测试用的spring测试文件,位置如下:

测试代码:

配置文件如下:

spring.profiles=it
  server.port=9898
  spring.h2.console.enabled=true
  spring.h2.console.path=/h2
  spring.datasource.driver-class-name=org.h2.Driver
  spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
  spring.datasource.username=sa
  spring.datasource.password=sa
  spring.datasource.max-wait=10000
  spring.datasource.max-active=5
  spring.datasource.test-on-borrow=true
  spring.datasource.test-while-idle = true
  spring.datasource.validation-query = SELECT 1
  # jpa
  spring.jpa.hibernate.ddl-auto=update
  #spring.jpa.hibernate.ddl-auto= create-drop
  spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
  spring.jpa.show-sql=true
  spring.jpa.generate-ddl=true

 这个配置文件的主要作用就是将程序连接的DB换成一个内存数据库h2,这样就不用在测试时受DB的掣肘了。

@RunWith(SpringRunner.class)
  @SpringBootTest
  @ActiveProfiles("it")
  @AutoConfigureMockMvc
  public class UserControllerTests {
      @Autowired
      private MockMvc mvc;
      @Autowired
      UserDao userDao;
      Long userId;
      @Before
      public void init(){
          User user = new User();
          user.setName("111");
          userId = userDao.save(user).getId();
          System.out.println(userId);
      }
      /**
       * 测试/user/{userId}
       * @throws Exception
       */
      @Test
      public void testGetUser() throws Exception {
          //success
          this.mvc.perform(get("/user/"+userId)).andExpect(status().isOk())
                  .andExpect(content().json("{ \"id\": "+userId+", \"name\": \"111\" }"));
          //fail
          this.mvc.perform(get("/user/"+(userId+100))).andExpect(status().isOk())
                  .andExpect(content().string("fail"));
      }
      /**
       * 测试login
       * @throws Exception
       */
      @Test
      public void exampleTest2() throws Exception {
          //success
          this.mvc.perform(post("/user/login").param("id",userId.toString()).param("pwd","11"))
                  .andExpect(status().isOk())
                  .andExpect(content().json("{ \"id\": "+userId+", \"name\": \"111\" }"));
          //fail
          this.mvc.perform(post("/user/login").param("id",userId.toString()+"11").param("pwd","11"))
                  .andExpect(status().isOk())
                  .andExpect(content().string("fail"));
      }
  }

@ActiveProfiles("it") 这个注解就是制定本测试代码加载的配置文件,”it“指 文件名 application-XX.properties 中间的xx,spring会自动根据名称去加载对应的配置文件。

init()?方法就是在内存数据库中构造自己需要的数据,这是集成测试最常见的步骤。

后面的测试代码就不需要解释太多了。

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值