Java JMockit无法模拟类的多个实例
我想创建一个映射,它将字符串作为键,并将类Java JMockit无法模拟类的多个实例,java,unit-testing,junit,mocking,jmockit,Java,Unit Testing,Junit,Mocking,Jmockit,我想创建一个映射,它将字符串作为键,并将类Candidate的模拟实例作为值 Map<String, Long> domainNameToId = new HashMap<String, Long>(); domainNameToId.put("farmaciapuentezurita.es", 1234l); domainNameToId.put("vivefarma.com", 2345l); domainNameToId.put("e
Candidate
的模拟实例作为值
Map<String, Long> domainNameToId = new HashMap<String, Long>();
domainNameToId.put("farmaciapuentezurita.es", 1234l);
domainNameToId.put("vivefarma.com", 2345l);
domainNameToId.put("eurofarmacia.com", 3456l);
Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>();
for(String domain : domainNameToId.keySet()) {
final Candidate cand = new MockUp<Candidate>() {
@Mock Long getDomainId() { return domainNameToId.get(domain); } // private method
@Mock boolean validateAndPrepare() { return true; }
@Mock String getRepresentingName() { return domain; }
}.getMockInstance();
expectedCandidates.put(domain, cand);
}
结果非常奇怪——第一个候选人被恰当地模仿了,而其他被模仿的候选人根本没有被模仿,他们真正的方法被调用了
我试图回到期望值API:
final Candidate cand = new Candidate(domain);
new Expectations(cand) {{
cand.getDomainId(); result = domainNameToId.get(domain); // Had to make it public :-(
cand.validateAndPrepare(); result = true;
cand.getRepresentingName(); result = domain;
}};
无济于事:
java.lang.IllegalArgumentException:已模拟:class com.urlservice.data.Candidate
在
我真的想升级到最新版本,但我找不到解决这个问题的方法
更新:我没有在1.28之前的任何版本中复制此问题,因此我猜这就是它引入的版本
此外,关于我的第二个示例(new MockUp(T targetInstance)
),我查看了classMockUp
第402行的源代码,在我看来,预期的行为是不模拟除第一个实例以外的任何特定目标实例:
MockUp<?> previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied();
if (previousMockUp != null) {
targetType = previousMockUp.targetType;
mockedClass = previousMockUp.mockedClass;
return; // Input param targetInstance is disregarded
}
MockUp previousMockUp=findPreviousFakedClassififMockupalReadyApplied();
如果(上一个实体模型!=null){
targetType=previousMockUp.targetType;
mockedClass=previousMockUp.mockedClass;
return;//忽略输入参数targetInstance
}
我错过了什么
更新2:我想出了一个失败的测试示例。这有点麻烦,但我相信它会解决问题的
public class SampleTest {
class TestedClass {
private IncrementingDependency dep;
TestedClass(IncrementingDependency dep) { this.dep = dep; }
public int getVal() { return dep.inc(); }
}
class IncrementingDependency {
int val;
public IncrementingDependency(int val) { this.val = val; }
public int inc() { return ++val; }
}
@Test
public void sampleTest() {
List<Integer> inputVals = Arrays.asList(1, 2, 3);
List<TestedClass> incrementingClasses = new ArrayList<TestedClass>();
for (Integer num : inputVals) {
IncrementingDependency dep = new IncrementingDependency(num);
new MockUp<IncrementingDependency>(dep) {
@Mock int inc() { return num; } // Mock with different behavior - DON'T INCREMENT
};
incrementingClasses.add(new TestedClass(dep));
}
assertThat(incrementingClasses.get(0).getVal()).isEqualTo(1); // Passes - 1 wasn't incremented (mocked behavior)
assertThat(incrementingClasses.get(1).getVal()).isEqualTo(2); // Fails - real code was called and 2 was incremented to 3
assertThat(incrementingClasses.get(2).getVal()).isEqualTo(3); // We never get to this point
}
}
公共类抽样测试{
类测试类{
私有增量依赖dep;
TestedClass(IncrementingDependency dep){this.dep=dep;}
public int getVal(){return dep.inc();}
}
类递增依赖项{
int-val;
公共增量依赖项(int-val){this.val=val;}
public int inc(){return++val;}
}
@试验
公共无效样本测试(){
List inputVals=Arrays.asList(1,2,3);
List incrementingClasses=new ArrayList();
for(整数num:inputVals){
IncrementingDependency=new IncrementingDependency(num);
新模型(dep){
@Mock int inc(){return num;}//具有不同行为的Mock-不递增
};
add(新测试类(dep));
}
assertThat(incrementingClasses.get(0.getVal()).isEqualTo(1);//传递-1未递增(模拟行为)
assertThat(incrementingClasses.get(1.getVal()).isEqualTo(2);//失败-调用了实代码,并将2增加到3
assertThat(incrementingClasses.get(2.getVal()).isEqualTo(3);//我们从来没有达到这一点
}
}
请注意,即使这个示例没有失败,在将依赖项传递给MockUp
的构造函数之前,我需要实例化依赖项这一事实充其量也是有问题的。创建模拟的全部意义不在于不需要实例化它吗?在仔细检查了JMockit的实际项目单元测试(非常清晰和有组织)之后,我设法以一种有点奇怪的方式解决了这个问题:
private void expectCandidatesFromMap(final Map<String, Long> domainNameToId) {
Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>();
class MockedCandidate extends MockUp<Candidate> {
private final String domainName;
private final Long domainId;
MockedCandidate(String domainName) {
this.domainName = domainName;
this.domainId = domainNameToId.get(domainName);
}
@Mock Long getDomainId() { return domainId; }
@Mock String getRepresentingName() { return domainName; }
@Mock boolean validateAndPrepare() { return true; }
}
for (String domain : domainNameToId.keySet()) {
expectedCandidates.put(domain, new MockedCandidate(domain).getMockInstance());
}
}
private void expectCandidatesFromMap(最终映射域名ID){
Map expectedCandidates=new HashMap();
类MockedCandidate扩展了MockUp{
私有最终字符串域名;
私有最终长域ID;
MockedCandidate(字符串域名){
this.domainName=域名;
this.domainId=domainNameToId.get(domainName);
}
@Mock Long getDomainId(){return domainId;}
@模拟字符串getRepresentingName(){返回域名;}
@模拟布尔validateAndPrepare(){return true;}
}
对于(字符串域:domainNameToId.keySet()){
expectedCandidates.put(域,新MockedCandidate(域).getMockInstance());
}
}
即使我的测试现在通过了,我也不完全理解为什么模拟类的特定实例的唯一方法是创建另一个特殊类(而不是像我在这个版本之前使用的那样匿名内联)
这种方法需要更多的行(私有字段声明、ctor实现),但可以说更加优雅
如果JMockit的一位贡献者能够对这件事有所了解,我们将不胜感激。我无法在使用新模型(cand)
时重现“奇怪”的结果。1.28似乎可以正常工作。您能展示一个失败的示例测试吗?请注意,forMock(T)
表示“仅影响给定实例”。因此,对Candidate
的任何其他实例调用的方法都不会转到@Mock
方法。@Rogério当然,这正是我想要实现的。我将尝试尽快提供一个失败的示例测试,但我最后放的JMockit代码示例是否有力地支持了我的说法?失败的示例测试在JMockit 1.20中也失败了;本部分从1.20更改为1.28。另外,我不明白测试的要点:为什么要模拟IncrementingDependency
?为什么“使用不同的行为进行模拟”?请注意,模拟(和伪造)的全部要点通常是,它们“模拟”或“模拟”模拟/伪造依赖项的实际(预期)行为,但不执行实际实现。您不会对不同的行为使用mock。实际上,这不是唯一的方法(请参见调用类)。没有必要像上面那样创建一个命名的实体模型;它只需要是一个“有状态”的模型(带有实例字段),仍然可以使用匿名子类编写。真正的问题是对不同的关联模拟实例多次应用相同的模拟类。这是不受支持的,因为它需要JMockit维护从模拟实例到模拟实例的内部映射。这在技术上是可行的,但不符合API的预期用途。因此,基本上,我在这里所做的是使用命名模型绕过API的预期用途?你能想象吗
private void expectCandidatesFromMap(final Map<String, Long> domainNameToId) {
Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>();
class MockedCandidate extends MockUp<Candidate> {
private final String domainName;
private final Long domainId;
MockedCandidate(String domainName) {
this.domainName = domainName;
this.domainId = domainNameToId.get(domainName);
}
@Mock Long getDomainId() { return domainId; }
@Mock String getRepresentingName() { return domainName; }
@Mock boolean validateAndPrepare() { return true; }
}
for (String domain : domainNameToId.keySet()) {
expectedCandidates.put(domain, new MockedCandidate(domain).getMockInstance());
}
}