??xml version="1.0" encoding="utf-8" standalone="yes"?>
@DataSet
public class UserDAOTest extends UnitilsJUnit4 {
@Test
public void testFindByName() {
User result = userDao.findByName("doe", "john");
assertPropertyLenEquals("userName", "jdoe", result);
}
@Test
public void testFindByMinimalAge() {
List<User> result = userDao.findByMinimalAge(18);
assertPropertyLenEquals("firstName", Arrays.asList("jack"), result);
}
}
@DateSet
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<usergroup name="admin" />
<user userName="jdoe" name="doe" firstname="john" userGroup="admin" />
<usergroup name="sales" />
<user userName="smith" name="smith" userGroup="sales" />
</dataset>
UserDAOTest.testFindByMinimalAge.xml
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<user userName="jack" age="18" />
<user userName="jim" age="17" />
</dataset>
public class UserDAOTest extends UnitilsJUnit4 {
@Test
@DataSet("UserDAOTest.testFindByMinimalAge.xml")
public void testFindByMinimalAge() {
List<User> result = userDao.findByMinimalAge(18);
assertPropertyLenEquals("firstName", Arrays.asList("jack"), result);
}
}
clean insertDbUnitModule.DataSet.loadStrategy.default
DbUnitModule.DataSet.loadStrategy.default=org.unitils.dbunit.datasetloadstrategy.InsertLoadStrategy
clean insertinsert
@DataSet
@DataSet(loadStrategy = InsertLoadStrategy.class)
DatabaseOperation
l CleanInsertLoadStrategy:
l InsertLoadStrategy:
l RefreshLoadStrategy:
l UpdateLoadStrategy:
multischema xml FlatXmlDataSet DataSetFactory
DataSetFactoryDbUnitModule.DataSet.factory.default @DataSet XlsDataSet
public class UserDAOTest extends UnitilsJUnit4 {
@Test @ExpectedDataSet
public void testInactivateOldAccounts() {
userDao.inactivateOldAccounts();
}
}
@ExpectedDataSet UserDAOTest.testInactivateOldAccounts-result.xml
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<user userName="jack" active="true" />
<user userName="jim" active="false" />
</dataset>
@DataSet className .methodName -result.xml
<?xml version='1.0' encoding='UTF-8'?>
<dataset xmlns="SCHEMA_A" xmlns:b="SCHEMA_B">
<user id="1" userName="jack" />
<b:role id="1" roleName="admin" />
</dataset>
SCHEMA_A SCHEMA_BSCHEMA_A SCHEMA_B SCHEMA_B SCHEMA_A
SCHEMA_A.user SCHEMA_B.role
database.schemaNames
database.schemaNames=SCHEMA_A, SCHEMA_B
SCHEMA_A
<?xml version='1.0' encoding='UTF-8'?>
<dataset xmlns:b="SCHEMA_B">
<user id="1" userName="jack" />
<b:role id="1" roleName="admin" />
</dataset>
DataSource
DataSource DataSource
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:@yourmachine:1521:YOUR_DB
database.userName=john
database.password=secret
database.schemaNames=test_john
unitils.properties unitils-local.properties
@TestDataSource DataSource
public abstract class BaseDAOTest extends UnitilsJUnit4 {
@TestDataSource
private DataSource dataSource;
@Before
public void initializeDao() {
BaseDAO dao = getDaoUnderTest();
dao.setDataSource(dataSource);
}
protected abstract BaseDAO getDaoUnderTest();
}
DatabaseUnitils.getDataSource()
l SELECT FOR UPDATE
l
l
DatabaseModule.Transactional.value.default=commit
rollback disabled
@Transactional
@Transactional(TransactionMode.ROLLBACK)
public class UserDaoTest extends UnitilsJUnit4 {
@Transactional
.........
配置 和大多数的项目一Punitils也需要一些配|,默认情况下有3个配|,每个配置都覆写前一个的配置 W一个配|文件unitils-default.propertiesQ它包含?jin)缺省值ƈ被包含在unitils的发行包中。我们没有必要对q个文gq行修改Q但它可以用来作参考?/p>
W二个配|文件unitils.propertiesQ它是我们需要进行配|的文gQƈ且能覆写~省的配|。D个例子,如果你的目使用的是oracle数据库,你可以创Z个unitils.properties文gq覆写相应的driver class和database url?/p>
q个文gq不是必ȝQ但是一旦你创徏?jin)一个,你就需要将该文件放|在目的classpath?/p>
最后一个文Ӟunitils-local.properties是可选的配置文gQ它可以覆写目的配|,用来定义开发者的具体讄QD个例子来_(d)如果每个开发者都使用自己的数据库schemaQ你可以创Z个unitils-local.properties为每个用户配|自q数据库̎受密码和schema?/p>
每个unitils-local.properties文g应该攄在对应的用户文g夹中Q?em>System.getProperty("user.home")Q?/p>
本地文g名unitils-local.properties也可以通过配置文g定义Q在unitils.properties覆写unitils.configuration.localFileName可以?/p>
启用你的unitils unitils的功能是依赖于基的测试框Ӟ要用unitils的功能,必denable他们Q这样做的目的也是ؓ(f)?jin)容易扩展。目前支持的框架有:(x) 举个例子Q如果用JUnit3Q你要用unitils 通常你将创徏你自q包含一些公p为的试基类Q如dataSource的注入,你可以让q个基类l承unitils试cR?/p>
当你使用的是JUnit4的话Q你也可是?em>@RunWith来代替(h)承unitils试c?/p>
模块pȝ 在开始D例之前,让我们先?jin)解一下unitils概念?/p>
unitils的结构被设计成了(jin)十分Ҏ(gu)扩展Q每个模块提供了(jin)一U服务,当执行Test的时候通过TestListener调用相应的服务?/p>
q种设计采用?jin)一个统一的方式提供服务,如果你需要加入其他的服务Q无需L~测试基c(UnitilsJUnit4q些c)(j)。要加入新的服务只需要添加一个新的模块ƈ在unitls配置文g中登记这个模块?/p>
目前unitils中所有有效的模块如下Q?/p>
unitils来源于一个尝试,是希望能以更务实的方式来看待单元测?..... q个指南?x)告诉你Q什么项目可以用unitils?q在q个指导斚w?/a> 中你可以?jin)解到测试的准侧和它的特炏V如果?zhn)想?jin)解如何可以配|unitils Qƈ得以q速地启动Q请查看cookbook ? unitils的断a 在开始这个指南之前我们先说明一下独立于unitils核心(j)模块的断a。在下面的例子中Q不需要进行配|,unitils的jar包和依赖包放在你的classpath下,可以进行测试了(jin)?/p> 通过反射q行断言 一个典型的单元试包含?jin)结果值和期望值的比较Qunitils提供?jin)断a的方法以帮助你进行该操作Q让我们看看实例2中对有着id、first name、last name属性的Usercȝ2个实例的比较 q在你的E序逻辑中是一个合乎逻辑的实玎ͼ当两个User实例拥有相同的id的时候,那么q两个实例就是相{的。然而这U方式在你的单元试中ƈ不合适,q不能通过id的相同来认ؓ(f)两个user是相同的?/p> q个断言会(x)成功Q但qƈ不是你所期望的,因此不要使用assertEquals来对两个对象q行判定是否相等Q外覆类和java.lang.Stringc除外)(j)。要x(chng)a他们相等Q一U方法就是断a每个属性相{?/p> unitils提供?jin)一些方法来帮助你执行断a更加的简单,通过反射Q?em>ReflectionAssert.assertRefEquals上面的代码重写如下:(x) 宽松的断a Z可维护性,q一Ҏ(gu)十分重要的,举例说明Q如果你要计一个帐L(fng)余额Q那你就没比较检查这个帐L(fng)名称。他只会(x)增加复杂性,使之更难理解。如果你惌你的试代码更容易生存,更容易重构的话,那请保你断a的范围?/p>
宽松的顺?/em> 在比较集合和数组的时候你可能q不兛_(j)他们中元素的序Q通过使用ReflectionAssert.assertRefEqualsҎ(gu)q?i>ReflectionComparatorMode.LENIENT_ORDER参数忽略元素的序?/p> 你所期望忽略的属性的对象必须攑֜断言左边Q如果放在右辚w么依然进行比较?/p> 宽松的日?/em> W三U从宽处理是ReflectionComparatorMode.LENIENT_DATESQ?/em>当两个日期都是|或者都是null的时候,实际的日期将?x)被忽略Q即断言为相{)(j)?/p> assertLenEquals ReflectionAssertq提供了(jin)一U断aQ他提供宽松的顺序又提供无视的忽略?/p> 属性断a assertLenEquals?i>assertRefEquals都是比较对象QReflectionAssert也提供方法对对象的属性进行比较。(依赖与ONGLQ?/p>
一些属性比较的例子 在这个方式中你期望的值和判定的对象也可以使用集合 引至QInfo中文|站http://www.infoq.com/cn/news/2007/04/savoia-tao-of-testing
]]>
database.driverClassName=oracle.jdbc.driver.OracleDriver
database.url=jdbc:oracle:thin:@yourmachine:1521:YOUR_DB
database.userName=john
database.password=secret
database.schemaNames=test_john
unitils.configuration.localFileName=projectTwo-local.properties
import org.unitils.UnitilsJUnit3;
public class MyTest extends UnitilsJUnit3 {
}
import org.junit.runner.RunWith;
import org.unitils.UnitilsJUnit4TestClassRunner;
@RunWith(UnitilsJUnit4TestClassRunner.class)
public class MyTest {
}
]]>
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertEquals(user1, user2);
你期望这个断a是成功的Q因两个实例含有相同的属性,但是q行的结果ƈ非如此,应ؓ(f)Usercdƈ没有覆写
equals()Ҏ(gu)Q所以assertEquals是对两个实例是否相等q行判断Quser1 == user2Q结果导致了(jin)比较的失败?/pre> 假设你像如下代码一样实C(jin)equalsҎ(gu)
public boolean equals(Object object) {
if (object instanceof User) {
return id == ((User) object).id;
}
return false;
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "Jane", "Smith");
assertEquals(user1, user2);
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertEquals(user1.getId(), user2.getId());
assertEquals(user1.getFirst(), user2.getFirst());
assertEquals(user1.getLast(), user2.getLast());
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertRefEquals(user1, user2);
q个断言通过反射对两个实例中的每个属性都q行比较Q先是id、然后是first name、最后是last name?/pre>
如果一个属性本w也是一个对象,那么会(x)使用反射q行递归比较Q这同样适合与集合、map、和数组之间的比较,他们
的每个元素会(x)通过反射q行比较。如果值是一个基本类型或者是一个外覆类Q那么将?x)?=q行值的比较Q因此下面的?br>a?x)取得成?/pre>assertRefEquals(1, 1L);
List<Double> myList = new ArrayList<Double>();
myList.add(1.0);
myList.add(2.0);
assertRefEquals(Arrays.asList(1, 2), myList);
List<Integer> myList = Arrays.asList(3, 2, 1);
assertRefEquals(Arrays.asList(1, 2, 3), myList, LENIENT_ORDER);
无视默认
W二U的从宽方式是?i>ReflectionComparatorMode.IGNORE_DEFAULTS模式Q当q种模式被设|的时?java
的默认|如null?、false会(x)不参与断aQ忽略)(j)?/pre> 举个例子Q如果你有一个Userc,该类有着first nameQlast nameQstreet{属性,但是你仅仅想对first name
和streetq行(g)查而忽略其他的属性?/pre>User actualUser = new User("John", "Doe", new Address("First street", "12", "Brussels"));
User expectedUser = new User("John", null, new Address("First street", null, null));
assertRefEquals(expectedUser, actualUser, IGNORE_DEFAULTS);
assertRefEquals(null, anyObject, IGNORE_DEFAULTS); // Succeeds
assertRefEquals(anyObject, null, IGNORE_DEFAULTS); // Fails
Date actualDate = new Date(44444);
Date expectedDate = new Date();
assertRefEquals(expectedDate, actualDate, LENIENT_DATES);
List<Integer> myList = Arrays.asList(3, 2, 1);
assertLenEquals(Arrays.asList(1, 2, 3), myList);
assertLenEquals(null, "any"); // Succeeds
assertLenEquals("any", null); // Fails
assertPropertyLenEquals("id", 1, user); //断言user的id属性的值是1
assertPropertyLenEquals("address.street", "First street", user); //断言user的address的street属?/pre>
assertPropertyLenEquals("id", Arrays.asList(1, 2, 3), users);
assertPropertyLenEquals("address.street", Arrays.asList("First street",
"Second street", "Third street"), users);
]]>
]]>