以前我写测试代码的时候,一直有个误区,对某类某方法的测试,有严重的依赖性,比如在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 静态方法生成
b、如果需要在相对复杂的测试用例中使用多个 Mock 对象,通过 IMocksControl 接口提供的 createMock 方法生成
第二步、设定 Mock 对象的预期行为和输出
第三步、将 Mock 对象切换到 Replay 状态
a、由a方式创建mork对象
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
第四步、调用 Mock 对象方法进行单元测试,并用断言验证
第五步、对 Mock 对象的行为进行验证
a、由a方式创建mork对象
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
第六步、为了避免生成过多的 Mock 对象,EasyMock 允许对原有 Mock 对象进行重新初始化
a、由a方式创建mork对象
b、由IMocksControl 接口提供的 createMock 方法生成mork对象
两种测试方式的对比
前一种方式,需要指定ContextConfiguration,并注入相关类
后一种方式采用EasyMock和Assert方式进行,但是在本类中提供属性的set方法。
如果希望测试某异常,本类中可抛出异常,测试中:
针对dao、service和controller的测试例子
需要依赖以下包:spring-mock--模拟MockHttpServletRequest、MockHttpServletResponse用;spring-test--ReflectionTestUtils设置属性用
dao:
service:
controller:
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("hj@163.com");
account1.setPassWord("1111");
List<Account> list = new ArrayList<Account>();
list.add(account1);
EasyMock.expect(hibernateTemplate.find(
"from Account where email=? and passWord=?",
new String[]{"hj@163.com","1111"})).andReturn(list).anyTimes();
EasyMock.replay(hibernateTemplate);
Account account = accountDao.getAccountByEmailAndPwd("hj@163.com", "1111");
EasyMock.verify(hibernateTemplate);
Assert.assertEquals("hj@163.com", 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("hj@163.com");
account.setPassWord("1111");
EasyMock.expect(accountDao.getAccountByEmailAndPwd("hj@163.com", "1111")).andReturn(account).anyTimes();
EasyMock.replay(accountDao);
Assert.assertEquals("hj@163.com", accountService.getAccountByEmailAndPwd("hj@163.com", "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", "hj@163.com");
request.setParameter("passWord", "1111");
Account account = new Account();
account.setEmail("hj@163.com");
account.setPassWord("1111");
EasyMock.expect(accountService.getAccountByEmailAndPwd("hj@163.com", "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());
}
}