Object 用Mockito模拟局部作用域对象的方法

Object 用Mockito模拟局部作用域对象的方法,object,mocking,local,mockito,Object,Mocking,Local,Mockito,我需要一些帮助: 例如: void method1{ MyObject obj1=new MyObject(); obj1.method1(); } 我想在我的测试中模拟obj1.method1(),但要透明,所以我不想修改代码。 在Mockito中有没有办法做到这一点?没有办法。您需要一些依赖项注入,即,与其实例化obj1,不如由某个工厂提供 myobject工厂; 公共无效setMyObjectFactory(MyObjectFactory) { 这个工厂=工厂; } vo

我需要一些帮助:

例如:

void method1{
    MyObject obj1=new MyObject();
    obj1.method1();
}
我想在我的测试中模拟
obj1.method1()
,但要透明,所以我不想修改代码。
在Mockito中有没有办法做到这一点?

没有办法。您需要一些依赖项注入,即,与其实例化
obj1
,不如由某个工厂提供

myobject工厂;
公共无效setMyObjectFactory(MyObjectFactory)
{
这个工厂=工厂;
}
void method1()
{
MyObject obj1=factory.get();
obj1.method();
}
那么您的测试将如下所示:

@测试
public void testMethod1()引发异常
{
MyObjectFactory=Mockito.mock(MyObjectFactory.class);
MyObject obj1=Mockito.mock(MyObject.class);
Mockito.when(factory.get()).thenReturn(obj1);
//模拟方法()
Mockito.when(obj1.method()).thenReturn(Boolean.FALSE);
SomeObject SomeObject=新的SomeObject();
setMyObject工厂(工厂);
method1();
//做一些断言
}

您可以通过在MyObject中创建工厂方法来实现这一点:

class MyObject {
    public static MyObject create() {
      return new MyObject();
    }
}
那就拿它开玩笑吧

但是,通过模拟局部作用域对象的方法,您将依赖于该方法的实现部分保持不变。因此,您失去了在不破坏测试的情况下重构该方法部分的能力。此外,如果您在模拟中存根返回值,那么您的单元测试可能会通过,但该方法在使用真实对象时可能会出现意外行为

总之,您可能不应该尝试这样做。相反,让测试驱动您的代码(也称为TDD),您将得到如下解决方案:

void method1(MyObject obj1) {
   obj1.method1();
}

传递依赖项,您可以轻松地为单元测试模拟该依赖项。

如果您确实希望避免接触此代码,可以使用(PowerMock for Mockito)


除此之外,您还可以以一种非常简单的方式使用它。

您可以避免更改代码(尽管我建议Boris的答案)并模拟构造函数,就像本例中模拟在方法内创建文件对象一样不要忘记将创建文件的类放入
@PrepareForTest

包hello.easymock.constructor;
导入java.io.File;
导入org.easymock.easymock;
导入org.junit.Assert;
导入org.junit.Test;
导入org.junit.runner.RunWith;
导入org.powermock.api.easymock.powermock;
导入org.powermock.core.classloader.annotations.PrepareForTest;
导入org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({File.class})
公共类构造函数示例测试{
@试验
public void testMockFile()引发异常{
//首先,为文件创建一个mock
最终文件fileMock=EasyMock.createMock(File.class);
expect(fileMock.getAbsolutePath()).andReturn(“/my/fake/file/path”);
EasyMock.replay(fileMock);
//然后,如果调用了构造函数,则返回模拟对象
类[]参数类型=新类[]{String.Class};
expectNew(File.class,parameterTypes,EasyMock.isA(String.class)).andReturn(fileMock);
replay(File.class);
//尝试构建一个真实的文件,并检查模拟是否启动
最后一个字符串mockedFilePath=新文件(“/real/path/for/File”).getAbsolutePath();
Assert.assertEquals(“/my/fake/file/path”,mockedFilePath);
}
}

来自@edutesoy的答案指向了
PowerMockito
的文档,并提到了构造函数模拟作为提示,但没有提到如何将其应用于问题中的当前问题

这里有一个基于此的解决方案。从问题中提取代码:

公共类MyClass{
无效方法1{
MyObject obj1=新的MyObject();
obj1.method1();
}
}
下面的测试将创建
MyObject
实例类的模拟,方法是使用
PowerMock
准备实例化它的类(在本例中,我称它为
MyClass
),并让
PowerMockito
存根
MyObject
类的构造函数,然后让您存根
MyObject
实例
method1()
调用:

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
公共类MyClassTest{
@试验
public void testMethod1(){
MyObject myObjectMock=mock(MyObject.class);
当(myObjectMock.method1()).thenReturn()时;
whenNew(MyObject.class).withNoArguments()。然后返回(myObjectMock);
MyClass objectTested=新建MyClass();
objectTested.method1();
…//此处显示您的断言或验证
}
}
这样,您的内部
method1()
调用将返回您想要的结果

如果您喜欢单行程序,可以通过创建模拟和存根内联来缩短代码:

MyObject myObjectMock=when(mock(MyObject.class.method1())。然后返回().getMock();

如果您不喜欢使用PowerMock,可以尝试以下方法:

public class Example{
...
void method1(){
    MyObject obj1 = getMyObject();
    obj1.doSomething();
}

protected MyObject getMyObject(){
    return new MyObject();
}
...
}
按照以下方式编写测试:

@Mock
MyObject mockMyObject;

@Test
void testMethod1(){
    Example spyExample = spy(new Example());
    when(spyExample.getMyObject()).thenReturn(mockMyObject);
    //stub if required
    doNothing().when(mockMyObject.doSomething());
    verify(mockMyObject).doSomething();
}

这就是我的想法,但这会添加更多不必要的代码,因为我需要模拟本地范围对象的方法。我不想创建工厂来创建myObject的新实例,而该代码是不必要的,因为它只需要模拟本地范围变量的方法。您必须有工厂才能有myObject的新实例
MyObject
只要启用了
method1
invoked@Boris巴甫洛维奇嗨,鲍里斯,我发现你的答案非常有趣,我正试图将它应用到我的一个例子中,这个例子与这个问题非常相似。但是我没办法通过考试。你能看一看吗:如果你能给我一些关于我做错了什么的提示,我将不胜感激