单元测试是分层测试理念中一个重要的环节,重点在于保证较小范围内某个功能模块的功能正常。
一般一个重要的测试类对应于一个单元测试(UT,unit testing),其中一个单元测试又包含正常和异常流程。
由于单元测试重点关注的是模块的功能实现,所以关联和依赖的其他服务可以被虚拟掉(mock),从而提高测试的精准性和速度。
在以java/spring为主的框架中,junit和mockito是常用的单元测试工具。
待测试的接口实现:
public class ToBeTestedServicezImpl implements ToBeTestedService {
protected DataBaseDAO dataBaseDAO;
protected FunctionSwitch functionSwitch;
@Override
public String returnString() {
return "a string";
}
@Override
public Object returnObject() {
functionSwitch.setAvaiable(true);
Object queriedData = null;
try {
queriedData = dataBaseDAO.readData("select * from test db;");
} catch (SQLException e) {
return new Result(false, "DB error.", null);
}
return new Result(true, "Switch turned on and queried successed.",
queriedData);
}
}
<pre name="code" class="java">public class ToBeTestedServiceUnitTest {
protected FunctionSwitch functionSwitch;
protected DataBaseDAO dataBaseDAO;
protected ToBeTestedService toBeTestedService;
@Before
public void init() {
functionSwitch = mock(FunctionSwitch.class);
dataBaseDAO = mock(DataBaseDAO.class);
toBeTestedService = new ToBeTestedServicezImpl();
((ToBeTestedServicezImpl) toBeTestedService)
.setDataBaseDAO(dataBaseDAO);
((ToBeTestedServicezImpl) toBeTestedService)
.setFunctionSwitch(functionSwitch);
}
@Test
public void testReturnString() {
try {
when(dataBaseDAO.readData(any(String.class))).thenReturn("a");
String result = toBeTestedService.returnString();
Assert.assertNotNull(result);
Assert.assertEquals("a string", result);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void testReturnObject() {
try {
when(dataBaseDAO.readData(any(String.class))).thenReturn("a");
Object result = toBeTestedService.returnObject();
Assert.assertNotNull(result);
Assert.assertEquals("a", ((Result) result).getData());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Test
public void testReturnObjectNull() {
try {
when(dataBaseDAO.readData(any(String.class))).thenReturn(null);
Object result = toBeTestedService.returnObject();
Assert.assertNotNull(result);
Assert.assertEquals(null, ((Result) result).getData());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
说明:
1.在测试类中首先要定义依赖的服务和待测试的服务。
2.将待测服务实例化。
3.依赖的服务使用mockito mock掉。(为方便地使用mockito提供的方法,将mockito静态引入,可以大量减少包名的引入。)
4.执行服务调用并断言。进行服务的判断。包括正常和异常流程。
感想:
1.单元测试的过程其实是一个严格的CR(code review)的过程,很多问题都可以在单元测试的过程中发现。
2.尽量由开发人员交叉完成。
3.是提高代码稳定性和系统覆盖率的有效方式。
4.持续集成的单元测试能够快速发现代码改动,并评估影响范围。