Java 在单元测试时,如何使用Mockito模拟来自另一个类的响应

Java 在单元测试时,如何使用Mockito模拟来自另一个类的响应,java,mockito,Java,Mockito,我有一个方法要测试,它调用另一个类来获取一些信息: public ClassToTest { public void methodToTest() { AnotherClass ac = Factory.getInstance(); ResponseObj response = ac.anotherMethod(); } } 另一个类是另一个JAR的一部分,我想模拟来自它的响应,使之成为特定的mock ResponseObj响应 如何使用Moc

我有一个方法要测试,它调用另一个类来获取一些信息:

public ClassToTest {

    public void methodToTest() {
        AnotherClass ac = Factory.getInstance();
        ResponseObj response = ac.anotherMethod();
    }
}
另一个类是另一个JAR的一部分,我想模拟来自它的响应,使之成为特定的mock ResponseObj响应


如何使用Mockito实现这一点?

首先需要使类可测试。这意味着您需要从methodToTest to instance字段中提取对象创建另一个类ac=Factory.getInstance,或者可能是单独的方法来模拟它,或者更好的方法是在类之外创建对象并通过构造函数传递它。因此,被测试的类应该如下所示:

public class ClassToTest {

    private AnotherClass ac;

    public ClassToTest(AnotherClass ac) {
        this.ac = ac;
    }

    public void methodToTest() {
        ResponseObj response = ac.anotherMethod();
        response.smth();
    }
}
然后,您需要将另一个类和ResponseObj声明为测试类中的字段,并将它们初始化为mock

AnotherClass ac = Mockito.mock(AnotherClass.class);
ResponseObj responseMock = Mockito.mock(ResponseObj.class);
之后,您可以模拟方法调用:

when(anotherClassMock.anotherMethod()).thenReturn(responseMock);
最后,您的测试类应该如下所示:

public class ClassToTestTest {

    private AnotherClass anotherClassMock = mock(AnotherClass.class);
    private ResponseObj responseMock = mock(ResponseObj.class);
    private ClassToTest classToTest = new ClassToTest(anotherClassMock);

    @Test
    public void test() {
        when(anotherClassMock.anotherMethod()).thenReturn(responseMock);

        classToTest.methodToTest();

        verify(responseMock, only()).smth();
    }
}
若您不能更改ClassToTest的公共API,您可以使用Mockito spy和protected方法的方法

public class ClassToTest {

    public void methodToTest() {
        AnotherClass ac = constructAnotherClassObj();
        ResponseObj response = ac.anotherMethod();
        response.smth();
    }

    protected AnotherClass constructAnotherClassObj() {
        return Factory.getInstance();
    }
}


public class ClassToTestTest {

    private AnotherClass anotherClassMock = mock(AnotherClass.class);
    private ResponseObj responseMock = mock(ResponseObj.class);
    private ClassToTest classToTest = spy(new ClassToTest());

    @Test
    public void test() {
        when(anotherClassMock.anotherMethod()).thenReturn(responseMock);
        when(classToTest.constructAnotherClassObj()).thenReturn(anotherClassMock);

        classToTest.methodToTest();

        verify(responseMock, only()).smth();
    }
}

首先,您需要使您的类可测试。这意味着您需要从methodToTest to instance字段中提取对象创建另一个类ac=Factory.getInstance,或者可能是单独的方法来模拟它,或者更好的方法是在类之外创建对象并通过构造函数传递它。因此,被测试的类应该如下所示:

public class ClassToTest {

    private AnotherClass ac;

    public ClassToTest(AnotherClass ac) {
        this.ac = ac;
    }

    public void methodToTest() {
        ResponseObj response = ac.anotherMethod();
        response.smth();
    }
}
然后,您需要将另一个类和ResponseObj声明为测试类中的字段,并将它们初始化为mock

AnotherClass ac = Mockito.mock(AnotherClass.class);
ResponseObj responseMock = Mockito.mock(ResponseObj.class);
之后,您可以模拟方法调用:

when(anotherClassMock.anotherMethod()).thenReturn(responseMock);
最后,您的测试类应该如下所示:

public class ClassToTestTest {

    private AnotherClass anotherClassMock = mock(AnotherClass.class);
    private ResponseObj responseMock = mock(ResponseObj.class);
    private ClassToTest classToTest = new ClassToTest(anotherClassMock);

    @Test
    public void test() {
        when(anotherClassMock.anotherMethod()).thenReturn(responseMock);

        classToTest.methodToTest();

        verify(responseMock, only()).smth();
    }
}
若您不能更改ClassToTest的公共API,您可以使用Mockito spy和protected方法的方法

public class ClassToTest {

    public void methodToTest() {
        AnotherClass ac = constructAnotherClassObj();
        ResponseObj response = ac.anotherMethod();
        response.smth();
    }

    protected AnotherClass constructAnotherClassObj() {
        return Factory.getInstance();
    }
}


public class ClassToTestTest {

    private AnotherClass anotherClassMock = mock(AnotherClass.class);
    private ResponseObj responseMock = mock(ResponseObj.class);
    private ClassToTest classToTest = spy(new ClassToTest());

    @Test
    public void test() {
        when(anotherClassMock.anotherMethod()).thenReturn(responseMock);
        when(classToTest.constructAnotherClassObj()).thenReturn(anotherClassMock);

        classToTest.methodToTest();

        verify(responseMock, only()).smth();
    }
}
尽管@arsen_adzhiametov的话是正确的,而且达到了标准,但我还是想谈谈我是如何做到这一点的

在本例中,我在模仿HomeClient的价值,它在内部是一个WebClient,为某些价值调用另一个服务

java请给它起个更好的名字

...
import org.mockito.Mockito;
...

class TestClass {
    HomeClient mockHomeClient;

    @BeforeEach
    void setup() {
        mockHomeClient = Mockito.mock(HomeClient.class);
        
        // Axon specific test code. Can ignore if not using Axon.
        fixture = new AggregateTestFixture<>(SomeAgreegate.class);
        fixture.registerInjectableResource(mockHomeClient);
    }
    
    @Test
    void testOpenOperation() throws HomeClientException {
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(2);
        // Do what you will with your code and call the method which you want to test
        // Mockito will mock the `HomeClient` in this case and `getXYZ` will return `2`
        // You can also change the mock response any time in the same function
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(-100);

        // Or specify different results on mock when different values are provided
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(1);
        Mockito.when(mockHomeClient.getXYZ("foo")).thenReturn(100);        
        Mockito.when(mockHomeClient.getXYZ("bar")).thenReturn(0);


    }
}
尽管@arsen_adzhiametov的话是正确的,而且达到了标准,但我还是想谈谈我是如何做到这一点的

在本例中,我在模仿HomeClient的价值,它在内部是一个WebClient,为某些价值调用另一个服务

java请给它起个更好的名字

...
import org.mockito.Mockito;
...

class TestClass {
    HomeClient mockHomeClient;

    @BeforeEach
    void setup() {
        mockHomeClient = Mockito.mock(HomeClient.class);
        
        // Axon specific test code. Can ignore if not using Axon.
        fixture = new AggregateTestFixture<>(SomeAgreegate.class);
        fixture.registerInjectableResource(mockHomeClient);
    }
    
    @Test
    void testOpenOperation() throws HomeClientException {
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(2);
        // Do what you will with your code and call the method which you want to test
        // Mockito will mock the `HomeClient` in this case and `getXYZ` will return `2`
        // You can also change the mock response any time in the same function
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(-100);

        // Or specify different results on mock when different values are provided
        Mockito.when(mockHomeClient.getXYZ("nice")).thenReturn(1);
        Mockito.when(mockHomeClient.getXYZ("foo")).thenReturn(100);        
        Mockito.when(mockHomeClient.getXYZ("bar")).thenReturn(0);


    }
}

可能会给你一些想法。@DawoodibnKareem这是一篇好文章+1可能会给你一些想法。@DawoodibnKareem这是一篇好文章+1如果我可以包含构造函数,这个解决方案将非常有效。你能建议一个解决方案,在不改变ClassToTested结构的情况下实现同样的效果吗?我对此有一些限制。您可能希望应用的第三个选项是PowerMock,它允许模拟静态方法调用,如Factory.getInstanceHanks以获取建议。我尝试了方法2,创建了一个受保护的方法和间谍。在这种情况下,responseMock总是为null。这是预期的吗?这里不应该有任何异常,除非您模拟的方法中没有任何异常。请记住,当您模拟方法调用时,如-when…,然后返回…-您的方法实际上已调用,只返回被mock替换的对象。有些情况下,当你根本不希望你的方法被执行时,所以你可以使用另一种存根方法-doReturn…当…分享更多关于你得到的异常的细节时。如果我可以包含构造函数,这个解决方案会非常好用。你能建议一个解决方案,在不改变ClassToTested结构的情况下实现同样的效果吗?我对此有一些限制。您可能希望应用的第三个选项是PowerMock,它允许模拟静态方法调用,如Factory.getInstanceHanks以获取建议。我尝试了方法2,创建了一个受保护的方法和间谍。在这种情况下,responseMock总是为null。这是预期的吗?这里不应该有任何异常,除非您模拟的方法中没有任何异常。请记住,当您模拟方法调用时,如-when…,然后返回…-您的方法实际上已调用,只返回被mock替换的对象。有些情况下,当您根本不希望执行您的方法时,所以您可以使用另一种存根方法-doReturn…当…共享有关异常的更多详细信息时。