编新的匹配器
jMock和Hamcrest提供了很多匹配器类和可以让你指定方法调用的可接受参数值的工厂函数.然而,有时预定义的约束不能让你指定一个期望足够精确表达你的意思或保持你的测试的灵活性.在这种情况下,你可以轻松地定义新的匹配器,可以无缝地扩展jMock中定义的一套存在的集合.
一个匹配器是一个实现了org.hamcrest.Matcher接口的对象.它做两件事情:
报告一个参数值是否满足约束(matches方法).
生成一个可读的表述来包含到测试失败消息中(describeTo方法从SelfDescribing接口继承).
来创建一个新的匹配器:
1.写一个类继承Hamcrest的BaseMatcher或TypeSafeMatcher基类.下列匹配器类测试一个字符串是否以一个给定的前缀开始.
import org.hamcrest.AbstractMatcher;
public class StringStartsWithMatcher extends TypeSafeMatcher<String> {
private String prefix;
public StringStartsWithMatcher(String prefix) {
this.prefix = prefix;
}
public boolean matchesSafely(String s) {
return s.startsWith(prefix);
[...]
在测试的Set-Up中覆盖期望定义
如果为了把测试下的对象放入已知的状态中你在一个测试的set-up中定义期望, 你有时想要在测试本身中忽略那些期望.例如, 你可能会在set-up中允许调用一些模拟对象,然后想要在测试中期望调用那些模拟对象.如果你在测试中仅仅定义附加期望, 在set-up中定义的允许期望优先考虑,测试期望将不会得到满足,因此测试失败.
在这种情况下,当期望有效时使用状态机来控制.这个状态机表现为测试状态,而不是在测试通信下的对象状态.
定义一个状态机,为它命名,例如:”test”.
在测试的set-up中,定义当”test”状态机不再”fully-set-up”状态中时期望为有效地.
在测试s的et-up最后,将”test”状态机移动到”fully-set-up”状态中.
和平时一样在你的测试中定义期望.
结果是在set-up中定义的仅应用于测试不在”running”中的期望将在测试本身中被忽略.
JUnit 3
public class ChildTest extends MockObjectTestCase {
States test = states("test");
Parent parent = mock(Parent.class);
// This is created in setUp
Child child;
@Override
public void setUp() {
[...]
状态
状态用于指定调用仅在一些状态通过其他期望调用起始或/和终止时会发生.一个测试可以定义多个状态机,一个调用在多个状态机的一个状态中可以约束发生.
定义新的状态机:
JUnit 3
final States state-machine-name = context.states("state-machine-name").startsAs("initial-state");
JUnit 4
final States state-machine-name = states("state-machine-name").startsAs("initial-state");
Other
final States state-machine-name = context.states("state-machine-name").startsAs("initial-state");
起始状态是可选的.如果没有指定,状态机会使用一个匿名的起始状态.
下列条款约束调用仅在明确的状态中发生,并定义了一个调用将如何改变状态机的当前状态.
when(state-machine.is(”state-name”));
约束最后一次期望调用仅当状态机是在命名状态中时发生
when(state-machine.isNot(”state-name”));
约束最后一次期望仅当状态机不再命名状态中时发生
then(state-machine.is(”state-name”));
当调用发生时改变状态机状态
例如:
final States pen = context.states("pen").startsAs("up");
oneOf (turtle).penDown(); then(pen.is("down"));
oneOf (turtle).forward(10); when(pen.is("down"));
oneOf (turtle).turn(90); when(pen.is("down"));
oneOf (turtle).forward(10); when(pen.is("down"));
oneOf (turtle).penUp(); then(pen.is("up"));
期望调用顺序
顺序常用来定义期望必须按照它们在测试代码中出现的顺序调用.一个测试可以创建多个顺序,一个期望可以依次是多个顺序的部分.
定义一个新的顺序:
JUnit 3
final Sequence sequence-name = sequence("sequence-name");
JUnit 4
final Sequence sequence-name = context.sequence("sequence-name");
Other
final Sequence sequence-name = context.sequence("sequence-name");
来期望一个调用顺序,按顺序编写期望并添加inSequence(sequence)语句到每一个期望.例如:
oneOf (turtle).forward(10); inSequence(drawing);
oneOf (turtle).turn(45); inSequence(drawing);
oneOf (turtle).forward(10); inSequence(drawing);
在顺序中的期望可以有任何调用次数.如果一个顺序中的一个期望被允许而不是被期望,它可以在顺序中被忽略.
期望方法多于(少于)一次
入门仅演示了期望对一个模拟对象的一个调用.测试经常需要使用不同基准的期望来允许一些方法调用发生,但如果不发生不会失败,期望方法被调用多次或不是全部,或忽略不相关的拟对象.
一个期望的调用次数定义了期望的这个方法允许被调用最小和最大次数.它在期望中的模拟对象前被指定.
invocation-count (mock).method(parameters); …
JMock定义了下列基准:
one
调用期望一次且仅一次.
exactly(n).of
调用期望正是n次.注意:one是exactly(1)方便的表达.
atLeast(n).of
调用期望至少n次.
atMost(n).of
调用期望最多n次.
between(min, max).of
调用期望至少min次,最多max次.
allowing
调用允许任何次但不必须发生.
ignoring
和allowing相同. Allowing和ignoring应该被选择来使测试代码清晰表达意图.
never
调用根本不期望.这会使测试更加明确和易懂.
Expecting vs. Allowing
expecting和allowing一个调用时最重要的区别是基准表达.
如果一个调用是允许的,在测试中它可以发生,但如果调用不发生,测试依然通过.如果一个调用被期望,则相反,它必须在测试中发生,如果它不发生,测试失败.当定义期望时你必须选择是期望还是允许每个调用.总的来说,我们发现如果你允许查询和期望命令测试可以保持灵活.一个查询是除了查询对象状态没有任何副作用的方法,一个命令是可以返回也可以不返回结果有副作用的方法. 当然,这并不适用于所有情况.如果你在测试中期望的方法被调用的次数没有其它约束遵循经验方法是有用的.