mysql google mock_各位都是怎么进行单元测试的?

贴一篇我专栏里的文章(一篇文章带你了解 Java 服务端单元测试的方方面面 - Not A Geek? - 知乎专栏),主要写的是 Java 单元测试。不过原理应该是相通的。

我在知乎专栏 Not A Geek? - 知乎专栏 会不定期写更新一些编程方面的文章,欢迎大家关注。

单元测试最直接的好处有两点:

1. 让你写出更好的代码:职业高内聚、低耦合而且接口设计合理的代码才易于测试;

2. 让你在修改代码时更有信心。

然后我们来举个例子,假设我们有个 add(a, b) 函数:

现在想测试它,于是用 JUnit 写个简单的测试函数:

你传入参数,调用 add 函数,然后很快能得到结果,对比期望值就能知道函数功能是否正常。这种验证方式叫做状态验证(state verification)。

但是真实项目中的代码要复杂的多。

比如,这段代码从数据库中查询数据:

如果要测试以上代码,你有几种选择:

1. 连接一个“真实”的数据库;

2. 选择内存数据库,比如 h2;

3. 创建 Mock 对象,在测试时代替 jdbcTemplate。

第 1 种方式最直接,不过依赖外部数据库会降低单元测试的效率,如果环境出现问题,比如网络不稳定,单元测试还会出错。第 2 种方式是我们推荐的做法,我们可以认为 h2 是 MySQL 的 fake 对象(当然你选用其他内存数据库,或者自己写一个内存数据库也可以 : P)。我们在生产环境使用 MySQL,而在单元测试/集成测试的时候,则选用 h2,当然这种方法也有一个问题:h2 不可能和 MySQL 完全兼容,所以我们需要改写部分不兼容的语句,或者用其他方式比如第 3 种方式测试它们。以上两种测试方法也是状态验证(state verification)。

我们来详细谈谈第 3 种方式。让我们看看 Mockito 版本的单元测试代码:

单元测试一般分为四个步骤:setup、exercise、verify、teardown。用上面的代码举例,setup 阶段,我们创建了 mock 对象,并且设置了 queryForInt 的行为;exercise 阶段调用了测试函数;verify 阶段做了两件事情:1. 确认 queryForInt 函数被正确调用,2. count() 返回值符合预期;teardown 阶段一般用来清理和释放资源,我们这里不需要,直接跳过了。

这种利用 Mock对象的测试叫做行为(behavior verification)。回到我们要测试的 count() 函数:我们调用了 jdbcTemplate 类的 queryForObject 函数从数据库中查询数据。我们要测试的是自己的业务逻辑,所以我们认为 jdbcTemplate 和数据库是可靠的(即使不可靠也不应该由我们的单元测试来验证),如果我们向 jdbcTemplate 传了正确的参数,后者就会向数据库发起正确的请求,然后得到正确的结果,换句话说,我们只要验证是否正确地调用了 jdbcTemplate 就行了。于是,我们可以创建一个模拟对象,即 Mock 对象,在测试的时候代替 jdbcTemplate。 这个对象可以完全由我们自己编写:实现特定接口,继承特定类,或者用动态代理,甚至修改字节码,它不会真正的访问数据库,但会保存你的调用行为,以便你来验证是否请发起了正确的请求。 当然,绝大部分情况, Mock 对象的实现细节不用你辛辛苦苦写出来。Java 社区有一大堆开源项目可以选择,比如:Mockito、EasyMock、PowerMock、JMock 和 JMockit 等。这么多工具,该如何选择呢,可以看看 stackoverflow 上的一个问题: What's the best mock framework for Java。简单的建议:使用 Mockito 结合 PowerMock,需要自己写 fake 对象时选择 JMockit。

测试中过程中,什么时候使用 Mock 对象,也形成了 Classical 和 Mockist 两种不同的测试风格。我个人以前是 Mockist 风格,现在偏向 Classical,不过这里不展开了,如果想进一步了解,可以看 Martin Fowler 的经典文章 Mocks Aren't Stubs。

说回到项目,实际的项目往往依赖了各种框架和组件,在动手为它们写 fake/mock 对象之前,可以看看社区是不是已经有了支持,比如:Spring 有 Spring Test、Spring MVC Test;涉及到 Zookeeper,Netflix 提供了in-process ZooKeeper server 。如果你使用 maven 等构件工具构件你的项目,你还可以利用构件工具以及它们的插件做更多事情,比如:利用多线程提高测试效率,只执行特定的测试代码,生成测试报告等等。通常,我们也会利用 jenkins 等持续集成工具定时/有代码变更时运行单元测试,保证修改不会破坏已有的代码功能。

另外,测试遗留代码也是一个巨大的挑战,你需要把代码重构到“可测试”的状态,《修改代码的艺术》在这方面一定可以帮到你。

扩展阅读:

1.《单元测试之道》

2.《修改代码的艺术》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值