java单元测试:unit testing best practices

常用技术: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来覆盖)

  1. Other framework libraries (you should assume they work correctly)
  2. The database (you should assume it works correctly when it is available)
  3. Other external resources (again you assume they work correctly when available)
  4. Really trivial code (like getters and setters for example)
  5. Code that has non deterministic results (Think Thread order or random numbers)
  6. Code that deals only with UI (e.g. Swing toolkit, Wicket)
  7. Don’t unit test I/O. I/O is for integrations. Use integration tests, instead.

Stub VS. Mock

区别

13918377-e55f315be8c37aab.png
image.png

桩模块用来模拟被测试的模块所调用的模块。驱动模块用来调用被测模块,模拟用户行为。


13918377-aceb918da0413963.png
image.png

用例子演示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的比较:

13918377-54ba468d5208e1a0.png
image.png

13918377-6b7306a469e4832d.png
image.png

参考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/

猜你喜欢

转载自blog.csdn.net/weixin_33785972/article/details/86944151
今日推荐