Mockito的when方法无法工作

33

我正在使用Mockito作为模拟框架。这里有一个情景,我的when(abc.method()).thenReturn(value)没有返回value,而是返回null。

public class DQExecWorkflowServiceImplTest {
@InjectMocks
DQExecWorkflowServiceImpl dqExecWorkflowServiceImpl = new DQExecWorkflowServiceImpl();
@Mock
private DQUtility dqUtility;
@Mock
private DqExec dqExec;
@Mock
private DqCntlDefn dqCntlDefn;
@Mock
private DqCntlWfDefn dqCntlWfDefn;
@Mock
private DqCntlWfDefnTyp dqCntlWfDefnTyp;
@Mock
private IDQControlWfDefTypeService controlWfDefTypeService;

@Before
public void setUp() throws Exception {
    dqExec = new DqExec();
    dqCntlWfDefn = new DqCntlWfDefn();
    dqUtility = new DQUtility();
    dqCntlWfDefnTyp = new DqCntlWfDefnTyp();
    dqCntlWfDefnTyp.setDqCntlWfDefnTypCd("MIN_INCLUSIVE_VAL");
    dqExecWorkflowServiceImpl
            .setControlWfDefTypeService(controlWfDefTypeService);

}

@Test
public void testExecuteWorkflow() {
    when(controlWfDefTypeService.getDqCntlWfDefnTypCd(dqCntlWfDefn))
            .thenReturn(dqCntlWfDefnTyp);
    dqExecWorkflowServiceImpl.executeWorkflow(dqExec, dqCntlWfDefn);
}

@Override
public DqCntlWfExec executeWorkflow(final DqExec dqExec,
        final DqCntlWfDefn dqCntlWfDefn) {
final DqCntlWfExec dqCntlWfExec = new DqCntlWfExec();
dqCntlWfExec.setDqCntlWfExecEffDt(dqUtil.getDefaultEffectiveDt());
dqCntlWfExec.setDqCntlWfExecExpDt(dqUtil.getDefaultExpiryDt());
dqCntlWfExec.setDqCntlWfDefn(dqCntlWfDefn);
dqCntlWfExec.setDqExec(dqExec);

final DqCntlWfDefnTyp dqCntlWfDefnTyp = controlWfDefTypeService
    .getDqCntlWfDefnTypCd(dqCntlWfDefn);
     String workflowType = null;
if(null!=dqCntlWfDefnTyp){
    workflowType = dqCntlWfDefnTyp.getDqCntlWfDefnTypCd();
}
当我运行测试文件时,when语句不起作用,我在buildpath中使用mockito1.8.5 jar。虽然服务调用被模拟了,但返回了null值。
final DqCntlWfDefnTyp dqCntlWfDefnTyp = controlWfDefTypeService
    .getDqCntlWfDefnTypCd(dqCntlWfDefn);

这个对象 dqCntlWfDefnTyp 是空的

我以前做过这个,当时没有问题,看起来对我以前处理过的文件也有效。我用相同的步骤处理测试文件,但是无法找出问题。请有人能帮忙吗?

提前感谢大家


58
尝试将其简化为较小的示例,因为它相当难以阅读。我确信那些名称不在您的控制范围内,因为setDqCntlWfExecEffDt根本没有任何借口。特别注意代码在使用缩写方面的不一致性:setDqCntlWfExecEffDtgetDefaultEffectiveDtexecuteWorkflowDqCntlWfExec等。说真的,这段代码可以直接提交到TheDailyWTF中。 - Sebastian Redl
代码已经进行了重构,使其更易读。 - Gopi
你能添加错误/输出吗? 你怎么知道它是“null”? 你是从workflowType推断出来的吗?.getDqCntlWfDefnTypCd()可能返回null吗?等等...顺便说一句,我认为原始代码可能是解决此问题的关键。:-) - Jonathan
我正在添加整个集合,因为我可能会错过一些信息。 - Gopi
谢谢,我已经解决了。 - Gopi
18
为什么有人会使用这样的非常规命名方式?有些人只是想看世界燃烧。 - Mukul Goel
8个回答

48

当我们松散地模拟对象时,Mockito模拟将起作用。

这是我所做的更改以使其起作用:

when(controlWfDefTypeService.getDqCntlWfDefnTypCd(any(DqCntlWfDefn.class))
    .thenReturn(dqCntlWfDefnTyp);

没有传递 Mock 类的对象,我传递了使用 Matcher any() 的类,它起作用了。


33

TL;DR 如果测试中的某些参数为null,请确保使用isNull()而不是any(SomeClass.class)来模拟参数调用。

public void SomeValue method(String string, SomeParam param)

不过,在测试中的调用大概是这样的:

method("some string during test", null);

现在,如果您使用以下方式模拟调用:

when(MockedClass.method(anyString(), any(SomeParam.class))

Mockito即使签名正确也不会匹配它。问题在于Mockito正在寻找具有参数StringSomeParammethod()调用,而实际调用是使用Stringnull。你需要做的是:

when(MockedClass.method(anyString(), isNull())

提示

由于不同框架中存在许多不同的 isNull() 实现,请务必使用此实现 org.mockito.ArgumentMatchers.isNull


6
你是一位没有斗篷的真正英雄。 - dtunctuncer
1
没错,完全同意!不是所有英雄都穿斗篷!只需使用any()即可同时处理实际对象参数和null。我需要这个答案来指导我为什么我的模拟行为与我预期的不同,特别是当传递null时。 - SourceVisor
1
这个答案救了我一天! - CN1002

10

我有同样的问题。对我来说解决方案是将Mockito.when(...).thenReturn(...);放到@Before-SetUp方法中。


8
我想我已经找到了您的问题,但并不全是我的功劳。
由于您试图在测试类中模拟'dqCntlWfDefnTyp',而该对象本身在您要测试的类中被实例化,因此必然会遇到一些问题。主要问题是该对象无法被模拟,因为它在测试期间被重新创建。
有几个选择,但我认为最好的选择是使用PowerMockito。您将能够用您模拟的对象替换正在被测试的类中的对象。
这种使用PowerMockito的优秀示例来自@raspacorp,可以参考此问题
public class MyClass {
void method1{
    MyObject obj1=new MyObject();
    obj1.method1();
}
}

而测试类...

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
@Test
public void testMethod1() {      
    MyObject myObjectMock = mock(MyObject.class);
    when(myObjectMock.method1()).thenReturn(<whatever you want to return>);   
    PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(myObjectMock);

    MyClass objectTested = new MyClass();
    objectTested.method1();

    ... // your assertions or verification here 
}
}

当我执行 objectTested = new MyClass(); 时,Spring 仍然使用自己创建的实例,而不是遵守在 Test 中配置的实例。有没有什么办法可以解决这个问题? - Saurabh Bhoomkar

3

就像Younes EO所说的那样:这通常与传递给模拟函数的null参数有关。

值得一提的是,如果您在测试中使用Kotlin(并且您的项目中有mockito-kotlin),对于Nullable参数,您应该通常使用anyOrNull()而不是any()


0

针对这个话题,如果有帮助的话,请注意。当我在断言anyString()时,我的mockito.when无法工作,原因是被测试类中的String是从属性文件中注入的值。以下代码在单元测试中设置了注入的值:

ReflectionTestUtils.setField(bean, "theVariable", "theValue");

0
在我的情况下,有一个我忘记模拟的方法: 当我模拟一个方法时,我传递的一个参数是另一个方法调用,我应该也模拟它,否则它会传递null,导致无法正常工作。 所以我进行了模拟: when(testService.create(any(VTestData.class), anyBoolean())).thenReturn(new VData()) 但是在服务中,这个方法的实现是这样的: VData vData = testService.create(adapter.from(data), true) 所以:adapter.from(data)没有被模拟,它传递的是null,所以它无法被识别为一个模拟的服务,因为它接收到的是null而不是一个对象。

0

当我为控制器进行测试时,我遇到了同样的问题。请使用@MockBean而不是@Mock。


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接