环境搭建
DAO测试
新建一个测试基类,后续所有的测试类继承该类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
@TransactionConfiguration(transactionManager = "transactionManager",defaultRollback = true)
@Transactional
public class BaseSpringTest {
}
public class UserDaoTest extends BaseSpringTest {
private final Logger logger = LoggerFactory.getLogger(UserDaoTest.class);
@Autowired
private UserDao userDao;
@Test
public void testSave() {
logger.info("testSave method in UserDaoTest");
User user = new User();
user.setName("test");
user.setBirthday(new Date());
user = this.userDao.save(user);
assert user.getId() > 0;
dbUserId = user.getId();
}
}
注意:dao的测试一定不能污染数据库,这里配置了TransactionConfiguration,在测试方法执行完毕后回滚事务。
Service测试
service层的测试通常依赖dao层,对于dao的依赖,使用mock框架mock出相应的bean,重心放在service方法的逻辑处理是否正确。
参考:《springockito-annotations做spring的单元测试》
Controller测试
外部接口测试
1.mock外部依赖;
2.新建被测试外部依赖接口service的实现(在test/java/src),使用单元测试的spring配置文件。
多线程测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class MultiThreadServiceTest {
private final Logger logger = LoggerFactory.getLogger(MultiThreadServiceTest.class);
@Autowired
private UserService userService;
@Test
public void multithreadSaveTest() throws InterruptedException {
for (int i=0;i<10;i++) {
Thread thread = new Thread(new WorkerTask());
thread.start();
latch.countDown(); // 线程启动后将latch数量减一,在WorkTask中会等待latch数量为0时执行数据库插入和查询操作。即保证所有线程同时执行数据库插入和查询操作。
}
// 这里要休眠一定时间,否则会导致所有WorkTask都没有执行。
// 因为Junit将测试方法执行完毕就退出了,这时候子线程其实还没开始执行。
Thread.sleep(3000L);
}
@After
public void teadDown() {
// 执行数据库清理工作。在多线程环境,在单元测试类上加入@Transactional并没有用,只对被测试方法本身所在的线程(主线程)有用,其他线程无效。
// 所以这里在单元测试执行完毕后,对数据库做清理工作。
userService.deleteAll(dbIds);
}
private CountDownLatch latch = new CountDownLatch(10);
private Collection<Long> dbIds = new ArrayList<>();
class WorkerTask implements Runnable {
@Override
public void run() {
try {
latch.await(); // 等待所有线程准备就绪同时执行数据库插入操作。
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
user.setName("test");
user.setBirthday(new Date());
user = userService.save(user);
assert user.getId() > 0;
dbIds.add(user.getId());
user = userService.findById(user.getId());
logger.info("user:{}",user);
Assert.assertNotNull(user);
Assert.assertEquals("test",user.getName());
}
}
}
注意:多线程环境单元测试,就算在测试类上加入@Transactional注解,数据库操作也是不会回滚的。原因可以查看代码注释,所以需要手动执行清理操作。