我编写了我的第一个单元测试,我认为它太依赖于其他模块,我不确定这是否是因为:
有个问题
首先,我要说,虽然我在开发方面有大约4年的经验,但我从未学过自动化测试,也从未教授过自动化测试。
我刚刚用Hibernate完成了DAL实现中的一个重大更改,我的一位同事建议我为新部件编写单元测试。
主要的变化是切换到每个请求的会话模式,以及更有建设性地使用应用程序事务。
由于上述更改的性质,单元测试从特定请求到达并开始事务的点开始,测试在事务结束后结束,并检查事务是否执行了它应该进行的更改。
这一测试涉及初始化以下对象:
内存中的
我认为我实际上已经编写了一个集成测试,因为我需要插入DB、Hibernate和存储库,但我不知道在其他情况下如何编写它,因为测试方法使用了所有这些对象作为操作,我很想看看事务处理是如何执行的(这是在测试方法上完成的)。
我很感激所有的评论和想法,如果它们不够清楚的话,我很乐意详细说明或澄清这些事情。
谢谢,
伊泰
HibernateSessionFactory实际上是Hibernate In Action书中众所周知的HibernateUtil,由于历史原因而命名错误。
public class AdminMessageRepositoryUpdaterTest {
private static WardId wardId;
private static EmployeeId employeeId;
private static WardId prevWardId;
private static EmployeeId prevEmployeeId;
@Test
public void testHandleEmployeeLoginToWard(){
AgentEmployeesWardsEngine agentEmployeesWardsEngine = new AgentEmployeesWardsEngine();
AgentEngine agentEngine = new AgentEngine();
//Remove all entries from AgentEmployeesWards table
HibernateSessionFactory.beginTransaction();
for (Agent agent : agentEngine.findAll()){
agentEmployeesWardsEngine.removeAgentEntries(agent.getId());
}
HibernateSessionFactory.commitTransaction();//no need to try catch as this is done in a controlled environment
int i=0;
//build expectedSet
Set<AgentEmployeesWards> expectedMappingsToChangeSet = new HashSet<AgentEmployeesWards>();
//Mappings which should have ward updated
expectedMappingsToChangeSet.add(new AgentEmployeesWards(new AgentId(1).getValue(), employeeId.getValue(), prevWardId.getValue(), true, TimestampUtils.getTimestamp(), i++));
expectedMappingsToChangeSet.add(new AgentEmployeesWards(new AgentId(2).getValue(), employeeId.getValue(), prevWardId.getValue(), true, TimestampUtils.getTimestamp(), i++));
//Mappings which should have employee updated
expectedMappingsToChangeSet.add(new AgentEmployeesWards(new AgentId(3).getValue(), prevEmployeeId .getValue(), wardId.getValue(), false, TimestampUtils.getTimestamp(), i++));
expectedMappingsToChangeSet.add(new AgentEmployeesWards(new AgentId(4).getValue(), prevEmployeeId.getValue(), wardId.getValue(), false, TimestampUtils.getTimestamp(), i++));
//Prepare clean data for persistence
Set<AgentEmployeesWards> cleanSet = new HashSet<AgentEmployeesWards>(expectedMappingsToChangeSet);
//Mappings which should NOT have ward updated
cleanSet.add(new AgentEmployeesWards(new AgentId(5).getValue(), employeeId.getValue(), prevWardId.getValue(), false, TimestampUtils.getTimestamp(), i++));
cleanSet.add(new AgentEmployeesWards(new AgentId(6).getValue(), employeeId.getValue(), prevWardId.getValue(), false, TimestampUtils.getTimestamp(), i++));
//Mappings which should NOT have employee updated
cleanSet.add(new AgentEmployeesWards(new AgentId(7).getValue(), prevEmployeeId .getValue(), wardId.getValue(), true, TimestampUtils.getTimestamp(), i++));
cleanSet.add(new AgentEmployeesWards(new AgentId(8).getValue(), prevEmployeeId.getValue(), wardId.getValue(), true, TimestampUtils.getTimestamp(), i++));
HibernateSessionFactory.beginTransaction();
for (AgentEmployeesWards agentEmployeesWards : cleanSet){
agentEmployeesWardsEngine.saveNewAgentEmployeesWardsEntry(agentEmployeesWards);
}
HibernateSessionFactory.commitTransaction();//no need to try catch as this is done in a controlled environment
//Close the session as to neutralize first-level-cache issues
HibernateSessionFactory.closeSession();
//Perform the action so it can be tested
AdminMessageReposityUpdater.getInstance().handleEmployeeLoginToWard(employeeId, wardId, TimestampUtils.getTimestamp());
//Close the session as to neutralize first-level-cache issues
HibernateSessionFactory.closeSession();
//Load actualSet from DAL
Set<AgentEmployeesWards> actualSet = new HashSet<AgentEmployeesWards>(agentEmployeesWardsEngine.findByPrimaryEmployeeId(employeeId));
actualSet.addAll(agentEmployeesWardsEngine.findByPrimaryWardId(wardId));
//Prepare expected
Set<AgentEmployeesWards> expectedSet = new HashSet<AgentEmployeesWards>();
for (AgentEmployeesWards agentEmployeesWards : expectedMappingsToChangeSet){
//We need to copy as the wardId and employeeId are properties which comprise the equals method of the class and so
//they cannot be changed while in a Set
AgentEmployeesWards agentEmployeesWardsCopy = new AgentEmployeesWards(agentEmployeesWards);
if (agentEmployeesWardsCopy.isEmployeePrimary()){
//If this is a employee primary we want it to be updated to the new org-unit id
agentEmployeesWardsCopy.setWardId(wardId.getValue());
} else {
//Otherwise we want it to be updated to the new employee id
agentEmployeesWardsCopy.setEmployeeId(employeeId.getValue());
}
expectedSet.add(agentEmployeesWardsCopy);
}
//Assert between actualSet and expectedSet
// Assert actual database table match expected table
assertEquals(expectedSet, actualSet);
}
@BeforeClass
public static void setUpBeforeClass() throws SQLException,ClassNotFoundException{
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:mem:MyCompany", "sa", "");
ConfigurationDAO configDAO = new ConfigurationDAO();
HibernateSessionFactory.beginTransaction();
configDAO.attachDirty(new Configuration("All","Log", "Level", "Info",null));
configDAO.attachDirty(new Configuration("All","Log", "console", "True",null));
configDAO.attachDirty(new Configuration("All","Log", "File", "False",null));
HibernateSessionFactory.commitTransaction();
Logger log = new Logger();
Server.getInstance().initialize(log);
Repository.getInstance().initialize(log);
AdminMessageReposityUpdater.getInstance().initialize(log);
AdminEngine adminEngine = new AdminEngine();
EmployeeEngine employeeEngine = new EmployeeEngine();
HibernateSessionFactory.beginTransaction();
Ward testWard = new Ward("testWard", 1, "Sales", -1, null);
adminEngine.addWard(testWard);
wardId = new WardId(testWard.getId());
Ward prevWard = new Ward("prevWard", 1, "Finance", -1, null);
adminEngine.addWard(prevWard);
prevWardId = new WardId(prevWard.getId());
Employee testEmployee = new Employee("testEmployee", "test", null, "employee", "f", prevWardId.getValue(), null, false, true);
employeeEngine.setEmployee(testEmployee);
employeeId = new EmployeeId(testEmployee.getId());
Employee prevEmployee = new Employee("prevEmployee", "prev", null, "employee", "f", wardId.getValue(), null, false, true);
employeeEngine.setEmployee(prevEmployee);
prevEmployeeId = new EmployeeId(prevEmployee.getId());
HibernateSessionFactory.commitTransaction();
HibernateSessionFactory.closeSession();
}
@AfterClass
public static void tearDownAfterClass(){
AdminEngine adminEngine = new AdminEngine();
EmployeeEngine employeeEngine = new EmployeeEngine();
HibernateSessionFactory.beginTransaction();
employeeEngine.removeEmployeeById(employeeId);
employeeEngine.removeEmployeeById(prevEmployeeId);
adminEngine.removeWardById(wardId);
adminEngine.removeWardById(prevWardId);
HibernateSessionFactory.commitTransaction();
HibernateSessionFactory.closeSession();
}
}发布于 2010-10-03 10:00:56
是的,这绝对是一个集成测试。使用集成测试没有任何错误,它们是测试策略的重要组成部分,但它们必须仅限于验证iif模块是否正确组装和配置是否正确设置。
如果您开始使用它们来测试功能,您将得到太多的它们,并且会发生两件非常糟糕的事情:
中
后一个问题是因为您现在正在集成测试中耦合您的设计,即使模块本身是完全解耦的。如果你找到了一个重构的机会,它很可能会打破十几个集成测试,要么你找不到勇气,要么管理会阻止你清理(“我工作!不要碰它”综合症)。
解决方案是通过“模拟”环境编写的所有部件进行单元测试。有很好的框架可以帮助动态地制作模拟对象,我个人经常使用EasyMock。然后,YOu描述了与世界其他地方的交互,同时验证了方法的功能。
在单元测试中,现在您将得到代码所依赖的依赖项的详细描述。您还将在这里发现设计问题,因为如果在单元测试中得到复杂的模拟行为,那么就意味着存在设计问题。这是一个很好的早期反馈。
对基础结构代码进行单元测试是没有意义的,因为它可能已经过了单元测试,而且无论如何您也无能为力。
然后添加1或2个有针对性的集成测试,以验证所有部件按预期工作,查询返回正确的对象,事务被正确处理,等等.
这平衡了在组装时验证所有工作的需要,并具有重构功能,保持测试时间短,并设计松散耦合的模块。
不过,要做到这一点,还需要一些经验。我建议找一位经验丰富的开发人员,以前就这样做过,并提供饮料以换取在这一领域的指导。问很多问题。
https://stackoverflow.com/questions/3849263
复制相似问题