常用技术:stub, mock
常用工具:Junit, TestNG; Jmockit, Powermock, Mockito
单元测试是如何发现bug的
假设有下面的类,可以判断一个数是不是质数(只有1和本身两个因数的数):
public class PrimeDecider {
final int number;
public PrimeDecider(int number) {
this.number = number;
}
public boolean isPrime() {
for (int n = 2; n * n <number; n++) {
if (number % n == 0) {
return false;
}
}
return true;
}
}
对应的单元测试
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class PrimDeciderTest {
@Test
public void sample_2_IsPrime() {
PrimeDecider decider = new PrimeDecider(2);
boolean itIsPrime = decider.isPrime();
assertTrue(itIsPrime);
}
@Test
public void sample_17_IsPrime() {
PrimeDecider decider = new PrimeDecider(17);
boolean itIsPrime = decider.isPrime();
assertTrue(itIsPrime);
}
@Test
public void sample_10_IsNotPrime() {
PrimeDecider decider = new PrimeDecider(10);
boolean itIsPrime = decider.isPrime();
assertFalse(itIsPrime);
}
@Test
public void sample_9_IsNotPrime() {
PrimeDecider decider = new PrimeDecider(9);
boolean itIsPrime = decider.isPrime();
assertFalse(itIsPrime);
}
}
运行结果发现sample_9_IsNotPrime是失败的,原因是PrimeDecider代码有BUG,少了一个等号, 修复的方法是修改判断质数的循环那个算数:
public boolean isPrime() {
for (int n = 2; n * n <=number; n++) {
if (number % n == 0) {
return false;
}
}
return true;
}
哪些需要单元测试?那些不需要单元测试
需要单元测试:
- Core code that is accessed by a lot of other modules
- Code that seems to gather a lot of bugs
- Code that changes by multiple different developers (often to accommodate new requirements)
不需要单元测试(同时需要考虑是不是需要用integration test来覆盖)
- Other framework libraries (you should assume they work correctly)
- The database (you should assume it works correctly when it is available)
- Other external resources (again you assume they work correctly when available)
- Really trivial code (like getters and setters for example)
- Code that has non deterministic results (Think Thread order or random numbers)
- Code that deals only with UI (e.g. Swing toolkit, Wicket)
- Don’t unit test I/O. I/O is for integrations. Use integration tests, instead.
Stub VS. Mock
区别
桩模块用来模拟被测试的模块所调用的模块。驱动模块用来调用被测模块,模拟用户行为。
用例子演示stub和mock的区别
有一个接口:
public interface Service {
// Get real data from database for example.
List findLanguages();
}
有个依赖接口的类,其中有如下代码
public CallService(Service service) {
this.service = service;
}
public List findLanguagesWithA() {
List languages = new ArrayList();
for (String s : service.findLanguages()) {
if (s.contains("a"))
languages.add(s);
}
return languages;
}
使用stub测试
打了一个桩(stub), 隔离对Service 接口的依赖:
@Test
public void whenCallServiceIsStubbed() {
CallService service = new CallService(new StubCallService());
assertTrue(service.findLanguagesWithA().size() == 1);
assertTrue(service.findLanguagesWithA().get(0).equals("Java"));
}
class StubCallService implements Service {
public List findLanguages() {
return Arrays.asList(
new String[] { "Groovy", "Clojure", "Java"});
}
}
使用mock测试
使用EasyMock对service接口进行mock:
@Test
public void whenCallServiceIsMocked() {
Service mock = createControl().createMock(Service.class);
CallService service = new CallService(mock);
expect(mock.findLanguages()).andReturn(Arrays.asList(
new String[] { "Groovy", "Clojure", "Java"}));
replay(mock);
List languages = service.findLanguagesWithA();
assertTrue(languages.size() == 1);
assertTrue(languages.get(0).equals("Java"));
verify(mock);
}
https://dzone.com/articles/testing-without-a-mock-framework
mock工具比较
来自于# JMockit的比较:
参考stackoverflow的说法, 目前比较流行的是JMockit、 PowerMock, 其次是 Mockito.
References
http://callistaenterprise.se/blogg/teknik/2010/11/12/stubs-n-mocks/
https://stackoverflow.com/questions/3459287/whats-the-difference-between-a-mock-stub
https://stackoverflow.com/questions/3459287/whats-the-difference-between-a-mock-stub/17810004#17810004
Test Double - Martin Fowler
Test Double - xUnit Patterns
Mocks Aren't Stubs - Martin Fowler
Command Query Separation - Martin Fowler
https://www.javaworld.com/article/2074508/core-java/mocks-and-stubs---understanding-test-doubles-with-mockito.html
https://www.hostettler.net/blog/2014/05/18/fakes-stubs-dummy-mocks-doubles-and-all-that/
http://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html#silimar
https://martinfowler.com/articles/mocksArentStubs.html
http://web.cs.iastate.edu/~smkautz/cs227f14/labs/lab5/page08.html
https://javax0.wordpress.com/2015/02/04/do-not-unit-test-bugs/
https://dzone.com/articles/unit-tests-dont-find-bugs
http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a
https://zeroturnaround.com/rebellabs/dont-test-blindly-the-right-methods-for-unit-testing-your-java-apps/
https://softwareengineering.stackexchange.com/questions/306277/testing-using-mocking-must-i-mock-all-dependencies-too
https://blog.codecentric.de/en/2017/07/mocks-real-thing-tips-better-unit-testing/