自打用Maven做项目管理之后,直接和UnitTest接触的次数增加,原以为已经三令五申要重视的测试总有那么一股bad smell,开始反思底层或者外围辅助不足,导致开发人员写测试代码压力大,决定着手改善。
一、测试工具由JUnit向TestNG转移
一直用Eclipse自带的JUnit工具,简单方便没得说,但几个有缺憾的地方一直不能改进。一个是static的BeforeClass、AfterClass,这已经臭名远播了,不多说。另一个是没有灵活的分组,为下一步要提到的分组测试带来不少麻烦。这两方面恰恰是TestNG的强项,非static的Before、After,可分组测试,甚至提供线程压力测试的控制等等一堆功能。缺点也有,Eclipse插件做的粗糙,创建Test类比Junit差了几个档次,基本信息都要手填,更没有对测试方法的选择和自动生成。权衡利弊还是坚持选择了TestNG
二、Dao层测试用不用DBUnit
DBUnit可以方便的完成备份表,插入测试数据,实际使用发现如果有外键关联表会有不少麻烦,往往对一个表的测试需要备份、创建N多关联表,再加上使用复杂sql、存储过程的场景,个人倾向DBUnit不强制使用。同时Dao层测试统一group(testNG的好处)
@Test(groups = { "dao" })
方便做有选择的测试
三、Mock测试
一般的Web开发其他层次都可以用Mock做到隔离Dao层,个人习惯用JMock,与TestNG配合良好,各种支持完善。
Mockery context = new Mockery(); UserService service = new UserServiceImpl(); UserDao userDao = context.mock(UserDao.class); ((UserServiceImpl) service).setUserDao(userDao); final User user = new User(); user.setId("123"); context.checking(new Expectations() { { (allowing(userDao)).getUserById("123"); will(returnValue(user)); (allowing(userDao)).getUserById("124"); will(returnValue(null)); } });
Service层可以轻松应付,完全独立测试,有麻烦的往往在Action层。基于Spring的AppliactionContext在Aciton内部getBean调用Service,造成在UnitTest中没有机会Mock,最后只好Mock掉ApplicationContext
ApplicationContext orgContext = SomeContainer.getApplicationContext(); ApplicationContext mockContext = context.mock(ApplicationContext.class); context.checking(new Expectations() { { (allowing(mockContext)).getBean("userService", UserService.class); will(returnValue(service)); (allowing(mockContext)).getBean(with(any(String.class)), with(any(Class.class))); will(new CustomAction("original getBean") { @Override public Object invoke(Invocation invocation) throws Throwable { return orgContext.getBean((String) invocation.getParameter(0), (Class<?>) invocation.getParameter(1)); } }); } });
针对Action逻辑,mock调用的Service,事后把mockContext植入到框架中。
常规的Action测试照旧,各种Mock,核对结果。
@Test(groups = { "action" })
四、整合的Test
项目内使用同一的测试基类,把配置导入之类的工作统统接管,减轻测试类压力,要注意全局导入方法要加上
@BeforeSuite(alwaysRun = true)
testsuite.xml可以有选择的执行某些group或全执行
<suite name="someproject" verbose="1"> <test name="testPackage"> <packages> <package name="cn.com.xxx.yyy.zzz.test.*" /> </packages> </test> </suite>
maven配置做个简单指定
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.6</version> <configuration> <skipTests>false</skipTests> <includes> <include>**/*Test.java</include> </includes> <!-- <groups>dao,action</groups>根据需要测试某些group --> <suiteXmlFiles> <suiteXmlFile>src/test/resources/testsuite.xml</suiteXmlFile> </suiteXmlFiles> </configuration> </plugin>