Java EasyMock:在集合顺序无关紧要的情况下,如何验证值集合的方法顺序
我有一个测试,其中我有一组特定的值,对于这些值,两个不同的方法将对集合中的每个值执行一次。我需要检查这两个方法是否按照特定的顺序相互调用,而不是按照值集的顺序调用。例如:Java EasyMock:在集合顺序无关紧要的情况下,如何验证值集合的方法顺序,java,junit,mocking,easymock,Java,Junit,Mocking,Easymock,我有一个测试,其中我有一组特定的值,对于这些值,两个不同的方法将对集合中的每个值执行一次。我需要检查这两个方法是否按照特定的顺序相互调用,而不是按照值集的顺序调用。例如: String[] values = { "A", "B", "C" }; for (...<loop over values...) { methodOne(value); methodTwo(value); } String[]值={“A”、“B”、“C”}; 对于(…您可以使用和answer()执
String[] values = { "A", "B", "C" };
for (...<loop over values...) {
methodOne(value);
methodTwo(value);
}
String[]值={“A”、“B”、“C”};
对于(…您可以使用和answer()
执行此操作
基本上,在methodOne()
的andAnswer()中,您设置了一些变量来保存传入的值
然后在andAnswer()
中,对于methodTwo()
您断言相同的参数与您从methodOne答案中保存的参数相匹配
由于每次调用methodOne
都会修改此变量,因此它将确保methodTwo()始终在methodOne()之后调用
注意此解决方案不是线程安全的
首先,您需要一些东西来保存methodOne调用中的变量。这可以是一个具有单个字段甚至一个元素数组的简单类。您需要这个包装器对象,因为您需要在IAnswer中引用它,而IAnswer需要一个final或final字段
private class CurrentValue{
private String methodOneArg;
}
现在是您的期望值。在这里,我调用了您正在测试的类(正在测试的系统)sut
:
String[] values = new String[]{"A", "B", "C"};
final CurrentValue currentValue = new CurrentValue();
sut.methodOne(isA(String.class));
expectLastCall().andAnswer(new IAnswer<Void>() {
@Override
public Void answer() throws Throwable {
//save the parameter passed in to our holder object
currentValue.methodOneArg =(String) EasyMock.getCurrentArguments()[0];
return null;
}
}).times(values.length); // do this once for every element in values
sut.methodTwo(isA(String.class));
expectLastCall().andAnswer(new IAnswer<Void>() {
@Override
public Void answer() throws Throwable {
String value =(String) EasyMock.getCurrentArguments()[0];
//check to make sure the parameter matches the
//the most recent call to methodOne()
assertEquals(currentValue.methodOneArg, value);
return null;
}
}).times(values.length); // do this once for every element in values
replay(sut);
... //do your test
verify(sut);
对于那些感兴趣的人,我使用预期的EasyMock功能解决了这个问题。解决方案是制作一个自定义IArgumentMatcher来验证一组值,并强制每个值连续匹配多少次。除了使用严格的模拟之外,自定义matcher还精确地解决了原始问题
public class SetMatcher implements IArgumentMatcher {
private List<String> valuesToMatch;
private List<String> remainingValues;
private String currentValue = null;
private int timesMatched = 0;
private int setMatches;
public SetMatcher(final List<String> valuesToMatch, final int times) {
this.valuesToMatch = new ArrayList<String>(valuesToMatch);
this.remainingValues = new ArrayList<String>(valuesToMatch);
this.setMatches = times;
}
public String use() {
EasyMock.reportMatcher(this);
return null;
}
public void appendTo(StringBuffer buffer) {
if (this.remainingValues.size() == 0) {
buffer.append("all values in " + this.valuesToMatch + " already matched " + this.setMatches + " time(s)");
} else {
buffer.append("match " + this.valuesToMatch + " " + this.setMatches + " time(s) each");
}
}
public boolean matches(Object other) {
if (this.timesMatched >= this.setMatches) {
this.currentValue = null;
this.timesMatched = 0;
}
if (null == this.currentValue) {
if (this.remainingValues.contains(other)) {
this.currentValue = (String) other;
this.timesMatched = 1;
this.remainingValues.remove(other);
return true;
}
} else if (this.currentValue.equals(other)) {
this.timesMatched++;
return true;
}
return false;
}
}
public类SetMatcher实现了IArgumentMatcher{
私人名单;
私有列表剩余值;
私有字符串currentValue=null;
私有int timesMatched=0;
私有int集匹配;
公共设置匹配器(最终列表值匹配,最终整数倍){
this.valueToMatch=新阵列列表(valueToMatch);
this.remainingValues=新的ArrayList(ValueToMatch);
this.setMatches=次;
}
公共字符串用法(){
EasyMock.reportMatcher(这个);
返回null;
}
公共无效附件(StringBuffer缓冲区){
if(this.remainingValues.size()==0){
buffer.append(“已匹配”+this.valueToMatch+“中的所有值”+this.setMatches+“时间”);
}否则{
buffer.append(“match”+this.valueToMatch+“”+this.setMatches+”每个时间);
}
}
公共布尔匹配(对象其他){
if(this.timesMatched>=this.setMatches){
this.currentValue=null;
此.timesMatched=0;
}
if(null==此.currentValue){
if(this.remainingValues.contains(其他)){
this.currentValue=(字符串)other;
此参数为1;
此.remainingValues.remove(其他);
返回true;
}
}else if(this.currentValue.equals(other)){
这个.timesMatched++;
返回true;
}
返回false;
}
}
正在测试的类:
public class DataProcessor {
private ServiceOne serviceOne;
private ServiceTwo serviceTwo;
public DataProcessor(ServiceOne serviceOne, ServiceTwo serviceTwo) {
this.serviceOne = serviceOne;
this.serviceTwo = serviceTwo;
}
public void processAll(List<String> allValues) {
List<String> copy = new ArrayList<String>(allValues);
for (String value : copy) {
this.serviceOne.preProcessData(value);
this.serviceTwo.completeTransaction(value);
}
}
}
公共类数据处理器{
私人服务一个服务一个;
私人服务2服务2;
公共数据处理器(服务一服务一、服务二服务二){
this.serviceOne=serviceOne;
this.serviceTwo=serviceTwo;
}
public void processAll(列出所有值){
列表副本=新的ArrayList(所有值);
for(字符串值:复制){
this.serviceOne.preProcessData(值);
this.service2.completTransaction(值);
}
}
}
以及测试:
public class DataProcessorTest {
List<String> TEST_VALUES = Arrays.asList("One", "Two", "Three", "Four", "Five");
@Test
public void test() {
IMocksControl control = EasyMock.createStrictControl();
ServiceOne serviceOne = control.createMock(ServiceOne.class);
ServiceTwo serviceTwo = control.createMock(ServiceTwo.class);
SetMatcher matcher = new SetMatcher(TEST_VALUES, 2);
for (int i = 0; i < TEST_VALUES.size(); i++) {
serviceOne.preProcessData(matcher.use());
serviceTwo.completeTransaction(matcher.use());
}
control.replay();
DataProcessor dataProcessor = new DataProcessor(serviceOne, serviceTwo);
dataProcessor.processAll(TEST_VALUES);
control.verify();
}
}
公共类DataProcessorTest{
列出测试值=数组。asList(“一”、“二”、“三”、“四”、“五”);
@试验
公开无效测试(){
IMocksControl control=EasyMock.createStrictControl();
ServiceOne ServiceOne=control.createMock(ServiceOne.class);
ServiceTwo ServiceTwo=control.createMock(ServiceTwo.class);
SetMatcher matcher=新的SetMatcher(测试值,2);
对于(int i=0;i
如果出现以下情况,测试将失败:
- ServiceOne和ServiceTwo的调用顺序错误
- ServiceOne和ServiceTwo不是使用相同的值连续调用的
- 使用不在指定值列表中的值调用ServiceOne或ServiceTwo
- 调用次数超过列表中某个值的预期次数
与自定义答案相比,这里似乎更适合自定义捕获,因为需要的是传入的值。但实际上是times()将确保正确的执行次数。@kurzweil4如果您是正确的,您可以使用Capture
类,但是如果您关心方法1总是在方法2之前调用,我认为您仍然需要andAnswer()
用于methodTwo()
我的想法是将它们捕获到一个有序列表中,然后在主方法完成后立即对所有值进行比较。我认为这不起作用,因为这只能证明方法的参数都是按相同的顺序调用的,而不是methodOne()是在methodTwo()之前调用的找到了使用预期EasyMock功能的解决方案。请参阅下文。感谢您的努力!@dkatzel
public class DataProcessorTest {
List<String> TEST_VALUES = Arrays.asList("One", "Two", "Three", "Four", "Five");
@Test
public void test() {
IMocksControl control = EasyMock.createStrictControl();
ServiceOne serviceOne = control.createMock(ServiceOne.class);
ServiceTwo serviceTwo = control.createMock(ServiceTwo.class);
SetMatcher matcher = new SetMatcher(TEST_VALUES, 2);
for (int i = 0; i < TEST_VALUES.size(); i++) {
serviceOne.preProcessData(matcher.use());
serviceTwo.completeTransaction(matcher.use());
}
control.replay();
DataProcessor dataProcessor = new DataProcessor(serviceOne, serviceTwo);
dataProcessor.processAll(TEST_VALUES);
control.verify();
}
}