1.什么是基于测试的开发
正常的开发流程:编码-->测试-->重复-->提交
基于测试的开发流程:测试-->编码-->重复-->提交
先写了测试之后,由于测试覆盖率要求为100%,所以就会让代码中可能存在的分支都进行测试,这样过先写测试单元,可以为将来的代码提供一种有效的参考
基于测试的开发例子:
编写接口:
package com.qunar.service;
import com.qunar.model.User;
public interface IUserService {
public void add(User user);
public void delete(String username);
public User loader(String username);
public User login(String username, String password);
}
2.针对接口进行测代码的编写 注意要覆盖每个方面
package com.qunar.service;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import com.qunar.model.User;
import com.qunar.model.UserException;
import com.qunar.service.impl.UserServiceByMap;
public class TestUserService {
private IUserService us;
private User baseUser;
@Before
public void setUp() {
// init
us = new UserServiceByMap();
baseUser = new User("admin", "123", "管理员");
}
@Test
public void testAdd() {
User user = baseUser;
us.add(user);
User user2 = us.loader("admin");
assertNotNull(user2);
assertTrue(user.equals(user2));
}
@Test
public void testDelete() {
us.add(baseUser);
User tUser = us.loader(baseUser.getUsername());
assertNotNull(tUser);
us.delete(baseUser.getUsername());
tUser = us.loader(baseUser.getUsername());
assertNull("删除的用户还存在",tUser);
}
@Test
public void testLogin() {
us.add(baseUser);
String username = "admin";
String password = "123";
User user = us.login(username, password);
assertTrue(user.equals(baseUser));
}
@Test(expected = UserException.class)
public void testNotExistsUserLogin() {
us.add(baseUser);
String username = "admin1";
String password = "123";
us.login(username, password);
}
@Test(expected = UserException.class)
public void testPasswordErrorUserLogin() {
us.add(baseUser);
String username = "admin";
String password = "1234";
us.login(username, password);
}
}
3.编写实现类
package com.qunar.service.impl;
import java.util.Map;
import com.google.common.collect.Maps;
import com.qunar.model.User;
import com.qunar.model.UserException;
import com.qunar.service.IUserService;
public class UserServiceByMap implements IUserService {
private Map<String, User> map = Maps.newHashMap();
@Override
public void add(User user) {
if (loader(user.getUsername())!=null) {
throw new UserException("用户名已经存在");
}
map.put(user.getUsername(), user);
}
@Override
public void delete(String username) {
map.remove(username);
}
@Override
public User loader(String username) {
// TODO Auto-generated method stub
return map.get(username);
}
@Override
public User login(String username, String password) {
User user = loader(username);
if(user==null){
throw new UserException("用户名不存在");
}
if(user.getPassword()!= password){
throw new UserException("用户密码不正确");
}
return user;
}
}
运行testCase 全部通过
如果在实现类中,当删除用户名为admin的时候不允许删除,如下所示
public void delete(String username) {
if(username.equals("admin")){
return ;
}
map.remove(username);
}
再次运行测试代码:
java.lang.AssertionError: 删除的用户还存在
at org.junit.Assert.fail(Assert.java:93)
at org.junit.Assert.assertTrue(Assert.java:43)
at org.junit.Assert.assertNull(Assert.java:551)
at com.qunar.service.TestUserService.testDelete(TestUserService.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
这样可以明确的指出那里出了错误
2.cobertura 的使用
1)首先修改工程的pom.xml文件 加入如下代码
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.5.1</version>
</plugin>
</plugins>
</reporting>
首先运行“mvn cobertura:help”, 如果不能运行,请添加以下仓库
<pluginRepositories>
<pluginRepository>
<id>Codehaus repository</id>
<url>http://repository.codehaus.org/</url>
</pluginRepository>
</pluginRepositories>
cobertura插件的命令
mvn cobertura:help 查看cobertura插件的帮助
mvn cobertura:clean 清空cobertura插件运行结果
mvn cobertura:check 运行cobertura的检查任务
mvn cobertura:cobertura 运行cobertura的检查任务并生成报表,报表生成在target/site/cobertura目录下
cobertura:dump-datafile Cobertura Datafile Dump Mojo
mvn cobertura:instrument Instrument the compiled classes
到项目根目录下运行mvn cobertura:cobertura 将会插桩class文件、测试、生成覆盖率报告
我的测试代码生产的报表如下: