以前我写测试代码的时候,一直有个误区,对某类某方法的测试,有严重的依赖性,比如在spring中测试server层某类时,会直接引入配置文件,注入相关类进行测试,这样的结果是代码换个环境,测试代码可能无法编译通过,后来才渐渐明白,测试代码应模拟出完全空白的环境,用EasyMock和Assert的方式进行测试。
why TDD?
Avoid overdesigning;
Find problems early in development process;
Write just the code you need;
Know when you are ready;
TDD-我的理解是测试先行,从客户体验的角度用测试用例来完成某个功能点,使用EasyMock提供的接口模拟,通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。
第一步:使用 EasyMock 生成 Mock 对象
a、由 org.easymock.EasyMock 类提供的 createMock 静态方法生成
ResultSet mockResultSet = createMock(ResultSet.class);
b、如果需要在相对复杂的测试用例中使用多个 Mock 对象,通过 IMocksControl 接口提供的 createMock 方法生成
IMocksControl control = EasyMock.createControl();
java.sql.Connection mockConnection = control.createMock(Connection.class);
java.sql.Statement mockStatement = control.createMock(Statement.class);
java.sql.ResultSet mockResultSet = control.createMock(ResultSet.class);
第二步、设定 Mock 对象的预期行为和输出
expect(mockResult.close()).times(3, 5);//该方法最少被调用3次,最多被调用5次
expect(mockResult.close()).times(3);//该方法被调用3次
expect(mockResult.close()).atLeastOnce();//该方法至少被调用一次
expect(mockResult.close()).anyTimes();//该方法可以被调用任意次
expect(mockResult.close()).andReturn("My return value");//预期返回值
expect(mockResult.close()).andThrow(Throwable throwable);//抛出异常
第三步、将 Mock 对象切换到 Replay 状态
a、由a方式创建mork对象
replay(mockResultSet);
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
control.replay();
第四步、调用 Mock 对象方法进行单元测试,并用断言验证
第五步、对 Mock 对象的行为进行验证
a、由a方式创建mork对象
verify(mockResultSet);
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
control.verify();
第六步、为了避免生成过多的 Mock 对象,EasyMock 允许对原有 Mock 对象进行重新初始化
a、由a方式创建mork对象
reset(mockResultSet);
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
control.reset();
两种测试方式的对比
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
import com.nokia.wellness.cms.SpringConfiguration;
import com.nokia.wellness.cms.persistence.entity.Account;
import com.nokia.wellness.cms.web.account.AccountConstants;
import com.nokia.wellness.cms.web.owner.controllers.ChangePasswordController;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { SpringConfiguration.LOCATION })
public class ChangePasswordControllerTest {
@Autowired
private ChangePasswordController controller;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp() throws Exception {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
@After
public void tearDown() throws Exception {
request = null;
response = null;
}
@Test
public void testChangePasswordNotEmpty() {
request.setMethod("POST");
request.setRequestURI(OwnerConstants.PREFIX + "/changePassword.do");
request.setParameter("oldPassword", "admin");
request.setParameter("newPassword", "admin");
Account account = new Account();
account.setUserName("admin");
request.getSession().setAttribute(AccountConstants.SESSION_LOGIN_ACCOUNT, account);
ModelAndView mv = null;
try {
mv = new AnnotationMethodHandlerAdapter().handle(request, response,
controller);
assertNotNull(mv.getViewName());
assertNotNull(request.getAttribute("changePasswordResult"));
} catch (Exception e) {
assertNull(mv);
}
}
}
前一种方式,需要指定ContextConfiguration,并注入相关类
import junit.framework.Assert;
import static org.easymock.EasyMock.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.code.morphia.Datastore;
import com.nokia.wellness.cms.MongoHelper;
import com.nokia.wellness.cms.SpringConfiguration;
import com.nokia.wellness.cms.service.impl.AccountServiceImpl;
import com.nokia.wellness.cms.service.impl.InitializeDataServiceImpl;
import com.nokia.wellness.cms.service.impl.ResourceServiceImpl;
import com.nokia.wellness.cms.service.impl.RoleServiceImpl;
import com.nokia.wellness.cms.service.impl.UserProfileServiceImpl;
public class IInitializeDataServiceTest {
private InitializeDataServiceImpl initializeDataService;
private IAccountService accountService;
private IResourceService resourceService;
private IRoleService roleService;
private IUserProfileService userProfileService;
private Datastore ds;
private MongoHelper mongoHelper;
@Before
public void setUp(){
try {
mongoHelper = new MongoHelper();
mongoHelper.start();
initializeDataService = new InitializeDataServiceImpl();
accountService = createMock(AccountServiceImpl.class);
resourceService = createMock(ResourceServiceImpl.class);
roleService = createMock(RoleServiceImpl.class);
userProfileService = createMock(UserProfileServiceImpl.class);
ds = createMock(Datastore.class);
initializeDataService.setAccountService(accountService);
initializeDataService.setResourceService(resourceService);
initializeDataService.setRoleService(roleService);
initializeDataService.setUserProfileService(userProfileService);
initializeDataService.setDs(ds);
} catch (Exception e) {
e.printStackTrace();
}
}
@After
public void tearDown(){
accountService = null;
resourceService = null;
roleService = null;
userProfileService = null;
ds = null;
initializeDataService = null;
mongoHelper.stop();
}
@Test
public void tsetInitializeData(){
//set the expect mock object
expect(accountService.existAccount()).andReturn(false).anyTimes();
expect(resourceService.existResource()).andReturn(true).anyTimes();
expect(roleService.existRole()).andReturn(false).anyTimes();
expect(userProfileService.existUserProfile()).andReturn(false).anyTimes();
expect(ds.getDB()).andReturn(mongoHelper.getMongo().getDB(SpringConfiguration.MONGO_DATABASE)).anyTimes();
//set the replay mock object
replay(accountService);
replay(resourceService);
replay(roleService);
replay(userProfileService);
replay(ds);
initializeDataService.initializeData();
//verify the mock objects
verify(accountService);
verify(resourceService);
verify(roleService);
verify(userProfileService);
verify(ds);
}
@Test
public void testExistData(){
expect(accountService.existAccount()).andReturn(true).anyTimes();
expect(resourceService.existResource()).andReturn(false).anyTimes();
expect(roleService.existRole()).andReturn(false).anyTimes();
expect(userProfileService.existUserProfile()).andReturn(false).anyTimes();
replay(accountService);
replay(resourceService);
replay(roleService);
replay(userProfileService);
Assert.assertEquals(true, initializeDataService.existData());
verify(accountService);
verify(resourceService);
verify(roleService);
verify(userProfileService);
}
}
后一种方式采用EasyMock和Assert方式进行,但是在本类中提供属性的set方法。
如果希望测试某异常,本类中可抛出异常,测试中:
@Test(expected=SomeException.class)
针对dao、service和controller的测试例子
需要依赖以下包:spring-mock--模拟MockHttpServletRequest、MockHttpServletResponse用;spring-test--ReflectionTestUtils设置属性用
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
dao:
package com.cpkf.notpad.dao.impl;
import java.util.ArrayList;
import java.util.List;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.test.util.ReflectionTestUtils;
import com.cpkf.notpad.dao.IAccountDao;
import com.cpkf.notpad.entity.Account;
public class AccountDaoTest {
private IAccountDao accountDao;
private HibernateTemplate hibernateTemplate;
@Before
public void setUp(){
accountDao = new AccountDaoImpl();
hibernateTemplate = EasyMock.createMock(HibernateTemplate.class);
ReflectionTestUtils.setField(accountDao, "hibernateTemplate", hibernateTemplate);
}
@After
public void tearDown(){
accountDao = null;
hibernateTemplate = null;
}
@Test
public void testGetAccountByEmailAndPwd(){
Account account1 = new Account();
account1.setEmail("[email protected]");
account1.setPassWord("1111");
List<Account> list = new ArrayList<Account>();
list.add(account1);
EasyMock.expect(hibernateTemplate.find(
"from Account where email=? and passWord=?",
new String[]{"[email protected]","1111"})).andReturn(list).anyTimes();
EasyMock.replay(hibernateTemplate);
Account account = accountDao.getAccountByEmailAndPwd("[email protected]", "1111");
EasyMock.verify(hibernateTemplate);
Assert.assertEquals("[email protected]", account.getEmail());
}
}
service:
package com.cpkf.notpad.server.impl;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.util.ReflectionTestUtils;
import com.cpkf.notpad.dao.IAccountDao;
import com.cpkf.notpad.dao.impl.AccountDaoImpl;
import com.cpkf.notpad.entity.Account;
import com.cpkf.notpad.server.IAccountService;
public class AccountServiceImplTest {
private IAccountDao accountDao;
private IAccountService accountService;
@Before
public void setUp(){
accountService = new AccountServiceImpl();
accountDao = EasyMock.createMock(AccountDaoImpl.class);
ReflectionTestUtils.setField(accountService, "accountDao", accountDao);
}
@After
public void tearDown(){
accountDao = null;
accountService = null;
}
@Test
public void testGetAccountByEmailAndPwd(){
Account account = new Account();
account.setEmail("[email protected]");
account.setPassWord("1111");
EasyMock.expect(accountDao.getAccountByEmailAndPwd("[email protected]", "1111")).andReturn(account).anyTimes();
EasyMock.replay(accountDao);
Assert.assertEquals("[email protected]", accountService.getAccountByEmailAndPwd("[email protected]", "1111").getEmail());
EasyMock.verify(accountDao);
}
}
controller:
package com.cpkf.notpad.controller.account;
import org.easymock.EasyMock;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.servlet.ModelAndView;
import com.cpkf.notpad.entity.Account;
import com.cpkf.notpad.server.IAccountService;
import com.cpkf.notpad.server.impl.AccountServiceImpl;
public class LoginControllerTest {
private IAccountService accountService;
private LoginController loginController;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp(){
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
loginController = new LoginController();
accountService = EasyMock.createMock(AccountServiceImpl.class);
ReflectionTestUtils.setField(loginController, "accountService", accountService);
}
@After
public void tearDown(){
accountService = null;
loginController = null;
request = null;
response = null;
}
@Test
public void testLogin(){
request.setMethod("POST");
request.setRequestURI("/login.do");
request.setParameter("email", "[email protected]");
request.setParameter("passWord", "1111");
Account account = new Account();
account.setEmail("[email protected]");
account.setPassWord("1111");
EasyMock.expect(accountService.getAccountByEmailAndPwd("[email protected]", "1111")).andReturn(account).anyTimes();
EasyMock.replay(accountService);
ModelAndView modelAndView = loginController.login(request, response);
EasyMock.verify(accountService);
System.out.println(modelAndView.getViewName());
Assert.assertNotNull(modelAndView.getViewName());
}
}