单元测试

单元测试

为什么要单元测试

首先我们了解下概念,什么是单元测试?

单元测试是由开发者编写的一小段代码,用于检验被测代码一个很小的,很明确的功能是否正确。用于判断某个条件(或特定场景下)的某个特定函数的行为是否正确。

执行单元测试,是为了证明某段代码的行为和开发者所期望的是否一致。因此,我们要测试的是规模很小的,非常独立的功能片段。通过对所有单独的部分行为建立起信心,确信他们都和我们期望的一致,然后我们才能开始组装和测试整个系统。

单元测试目的

其实只有一个目的,提高代码健壮性,减少bug率,减少线上故障。

单元测试应该怎么做?

  • 单元测试应该是全自动的
  • 单元测试应该是独立的
  • 单元测试是可以重复执行的,不能受到外界环境的影响。
  • 单元测试粒度应该足够小,一般是方法级别,最多是类级别
  • 为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。

测试框架

  • Mockito:https://site.mockito.org/
    Mockito是一个针对Java的mocking框架。它与EasyMock和jMock很相似,但是通过在执行后校验什么已经被调用,它消除了对期望行为(expectations)的需要。其它的mocking库需要你在执行前记录期望行为(expectations),而这导致了丑陋的初始化代码
  • PowerMock:http://powermock.github.io/
    PowerMock 也是一个单元测试模拟框架,它是在其它单元测试模拟框架的基础上做出的扩展。通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。因为 PowerMock 在扩展功能时完全采用和被扩展的框架相同的 API, 熟悉 PowerMock 所支持的模拟框架的开发者会发现 PowerMock 非常容易上手。PowerMock 的目的就是在当前已经被大家所熟悉的接口上通过添加极少的方法和注释来实现额外的功能,目前,PowerMock 仅支持 EasyMock 和 Mockito。

项目实践

boot项目 首先定义BaseTest基类

@SpringBootTest
@ActiveProfiles("unit")
@Slf4j
@Transactional
@Ignore
public class BaseTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();


}

加上@Transactional当测试类有数据操作时,跑完单元测试后会自动回滚事物
@Ignore注解用来忽略BaseTest类,因为一般我们不会在基类上面写具体的单元测试
ActiveProfiles指定我们用来跑单元测试的配置文件。可以单独在resources目录下面放一个application-unit.properties文件。一般配置文件prod(生产),test(测试环境),local(本地)比较合适
这里我们项目依赖的测试库为:

<dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
</dependency> 

@MockBean:
当我们需要mock某个外部类的作用时 可以使用这个注解。一般我们的测试类只会针对当前类。所以这里我们一般可以这么写:

/**
 * @Description: TODO
 * @Author jerry
 * @Date 2020/8/12 11:15 上午
 **/
public class HelloTest extends BaseTest {

    @Autowired
    TestClass testClass;

    @MockBean
    HelloService helloService;


    @Test
    public  void test(){
    Mockito.doReturn("test return").when(helloService).satHello(Mockito.anyString());
    testClass.doWork();
    }
}

可以看看MockBean实际注入的是什么样的:

/**
 * @Description: TODO
 * @Author jerry
 * @Date 2020/8/12 11:15 上午
 **/
public class HelloTest extends BaseTest {


    @MockBean
    HelloService helloService;


    @Test
    public  void testBeanDiff(){
        System.out.println(helloService.getClass().getName());
    }
}

打印结果:
com.wosai.upay.job.biz.HelloService$$EnhancerByMockitoWithCGLIB$$eda82ae2
可以发现他最终注入的是一个被动态增强的实例,用的是CGLIB字节码增强。$$EnhancerByMockitoWithCGLIB$$注意看这个名字。

题外
有的项目也会用到@SpyBean.对于这两个注解而言:
@MockBean 和 @SpyBean
@MockBean 得到的对象完全被 mock,如果没有 mock 方法的返回值,则默认返回空值
@SpyBean 得到的对象是真实的对象,只对设置 mock 返回值的方法起作用
一般情况下,建议使用 @MockBean,如果确实有需要 mock 部分接口可以使用 @SpyBean。

如果是非boot项目呢?我们来看看spring应用运行在外部容器的例子:

@ContextConfiguration(locations = {"classpath:spring/business-config.xml"})
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.crypto.*", "javax.net.ssl.*"})
@WebAppConfiguration
@Ignore
public class BaseTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();
}

ContextConfiguration指定加载我们spring上下文的配置文件
这里我们用的是PowerMock来跑我们的单元测试。具体单元测试类的写法上跟上面相差无几。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值