Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/343.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 用mockito嘲弄单身汉_Java_Unit Testing_Junit_Mocking_Mockito - Fatal编程技术网

Java 用mockito嘲弄单身汉

Java 用mockito嘲弄单身汉,java,unit-testing,junit,mocking,mockito,Java,Unit Testing,Junit,Mocking,Mockito,我需要测试一些遗留代码,它在方法调用中使用单例。该测试的目的是确保clas sunder测试调用Singleton方法。 我也看到过类似的问题,但所有的答案都需要其他依赖项(不同的测试框架)——不幸的是,我只限于使用Mockito和JUnit,但在这种流行的框架中,这应该是完全可能的 单身人士: public class FormatterService { private static FormatterService INSTANCE; private Formatter

我需要测试一些遗留代码,它在方法调用中使用单例。该测试的目的是确保clas sunder测试调用Singleton方法。 我也看到过类似的问题,但所有的答案都需要其他依赖项(不同的测试框架)——不幸的是,我只限于使用Mockito和JUnit,但在这种流行的框架中,这应该是完全可能的

单身人士:

public class FormatterService {

    private static FormatterService INSTANCE;

    private FormatterService() {
    }

    public static FormatterService getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new FormatterService();
        }
        return INSTANCE;
    }

    public String formatTachoIcon() {
        return "URL";
    }

}
被测类别:

public class DriverSnapshotHandler {

    public String getImageURL() {
        return FormatterService.getInstance().formatTachoIcon();
    }

}
单元测试:

public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

        when(FormatterService.getInstance()).thenReturn(formatter);

        when(formatter.formatTachoIcon()).thenReturn("MockedURL");

    }

    @Test
    public void testFormatterServiceIsCalled() {

        DriverSnapshotHandler handler = new DriverSnapshotHandler();
        handler.getImageURL();

        verify(formatter, atLeastOnce()).formatTachoIcon();

    }

}
public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

        when(formatter.formatTachoIcon()).thenReturn("MockedURL");

    }

    @Test
    public void testFormatterServiceIsCalled() {

        DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter);
        handler.getImageURL();

        verify(formatter, times(1)).formatTachoIcon();

    }

}
这个想法是为了配置可怕的单例的预期行为,因为被测试的类将调用它的getInstance,然后调用Formattachicon方法。不幸的是,此操作失败,并显示错误消息:

when() requires an argument which has to be 'a method call on a mock'.

您的getInstance方法是静态的,因此不能使用mockito进行模拟。您可能希望使用来执行此操作。虽然我不建议这样做。我将通过依赖项注入测试DriverSnapshotHandler:

public class DriverSnapshotHandler {

    private FormatterService formatterService;

    public DriverSnapshotHandler(FormatterService formatterService) {
        this.formatterService = formatterService;
    }

    public String getImageURL() {
        return formatterService.formatTachoIcon();
    }

}
单元测试:

public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

        when(FormatterService.getInstance()).thenReturn(formatter);

        when(formatter.formatTachoIcon()).thenReturn("MockedURL");

    }

    @Test
    public void testFormatterServiceIsCalled() {

        DriverSnapshotHandler handler = new DriverSnapshotHandler();
        handler.getImageURL();

        verify(formatter, atLeastOnce()).formatTachoIcon();

    }

}
public class TestDriverSnapshotHandler {

    private FormatterService formatter;

    @Before
    public void setUp() {

        formatter = mock(FormatterService.class);

        when(formatter.formatTachoIcon()).thenReturn("MockedURL");

    }

    @Test
    public void testFormatterServiceIsCalled() {

        DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter);
        handler.getImageURL();

        verify(formatter, times(1)).formatTachoIcon();

    }

}
您可能希望在@After方法中将mock设置为null。
这是IMHO更干净的解决方案。

您所要求的是不可能的,因为您的遗留代码依赖于静态方法
getInstance()
,并且Mockito不允许模拟静态方法,因此下面的行将不起作用

when(FormatterService.getInstance()).thenReturn(formatter);
解决此问题有两种方法:

  • 使用不同的模拟工具,例如PowerMock,它允许模拟静态方法

  • 重构您的代码,这样您就不会依赖于静态方法。我能想到的最不具侵入性的方法是向
    driversnapshotdhandler
    添加一个构造函数,该构造函数注入
    FormatterService
    依赖项。此构造函数将仅在测试中使用,并且您的生产代码将继续使用真正的单例实例

    public static class DriverSnapshotHandler {
    
        private final FormatterService formatter;
    
        //used in production code
        public DriverSnapshotHandler() {
            this(FormatterService.getInstance());
        }
    
        //used for tests
        DriverSnapshotHandler(FormatterService formatter) {
            this.formatter = formatter;
        }
    
        public String getImageURL() {
            return formatter.formatTachoIcon();
        }
    }
    
  • 然后,您的测试应该如下所示:

    FormatterService formatter = mock(FormatterService.class);
    when(formatter.formatTachoIcon()).thenReturn("MockedURL");
    DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter);
    handler.getImageURL();
    verify(formatter, atLeastOnce()).formatTachoIcon();
    

    我认为这是可能的。看一个例子

    测试前:

    @Before
    public void setUp() {
        formatter = mock(FormatterService.class);
        setMock(formatter);
        when(formatter.formatTachoIcon()).thenReturn(MOCKED_URL);
    }
    
    private void setMock(FormatterService mock) {
        try {
            Field instance = FormatterService.class.getDeclaredField("instance");
            instance.setAccessible(true);
            instance.set(instance, mock);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    测试后-清理类很重要,因为其他测试会与模拟实例混淆

    @After
    public void resetSingleton() throws Exception {
       Field instance = FormatterService.class.getDeclaredField("instance");
       instance.setAccessible(true);
       instance.set(null, null);
    }
    
    测试:

    @Test
    public void testFormatterServiceIsCalled() {
        DriverSnapshotHandler handler = new DriverSnapshotHandler();
        String url = handler.getImageURL();
    
        verify(formatter, atLeastOnce()).formatTachoIcon();
        assertEquals(MOCKED_URL, url);
    }
    
    如果它能帮助某人 这是我测试单例类的方法 您只需要模拟所有的单例类,然后使用doCallRealMethod来真正调用您想要测试的方法

    SingletonClass.java:

    class SingletonClass {
    
        private static SingletonClass sInstance;
    
        private SingletonClass() {
            //do somethings
        }
    
        public static synchronized SingletonClass getInstance() {
            if (sInstance == null) {
                sInstance = new SingletonClass();
            }
    
            return sInstance;
        }
    
        public boolean methodToTest() {
            return true;
        }
    }
    
    SingletonClassTest.java:

    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.Mockito;
    
    import static org.junit.Assert.assertTrue;
    import static org.mockito.Mockito.mock;
    
    public class SingletonClassTest {
    
        private SingletonClass singletonObject;
    
        @Before
        public void setUp() throws Exception {
            singletonObject = mock(SingletonClass.class);
    
            Mockito.doCallRealMethod().when(singletonObject).methodToTest();
        }
    
        @Test
        public void testMethodToTest() {
            assertTrue(singletonObject.methodToTest());
        }
    }
    

    我有一个使用反射模拟单例类的变通方法。在设置测试时,您可以考虑执行以下操作:p>
    @Mock 
    private MySingletonClass mockSingleton;
    
    private MySingletonClass originalSingleton;
    
    @Before 
    public void setup() {
        originalSingleton = MySingletonClass.getInstance();
        when(mockSingleton.getSomething()).thenReturn("Something"); // Use the mock to return some mock value for testing
    
        // Now set the instance with your mockSingleton using reflection 
        ReflectionHelpers.setStaticField(MySingletonClass.class, "instance", mockSingleton);
    }
    
    @After
    public void tearDown() {
        // Reset the singleton object when the test is complete using reflection again
        ReflectionHelpers.setStaticField(MySingletonClass.class, "instance", null);
    }
    
    @Test
    public void someTest() {
        // verify something here inside your test function.
    }
    

    ReflectionHelpers
    由Android中的
    Robolectric
    提供。但是,您可以编写自己的函数来帮助您实现这一点。你可以试着想出一个主意

    在我看来,作为软件开发的初学者,在驱动程序/其他服务中注入单例类的依赖项是一个不错的选择。 因为我们可以控制类的单个实例的创建,并且仍然能够模拟静态方法(正如您可能已经猜到的,我脑海中有util服务),而无需使用PowerMock之类的东西来模拟静态方法(IME,这有点痛苦) 我非常愿意从可靠或良好的OO设计原则的角度听取有经验的人的意见

    public class DriverSnapshotHandler {
        private FormatterService formatter;
        public DriverSnapshotHandler() {
            this(FormatterService.getInstance());
        }
        public DriverSnapshotHandler (FormatterService formatterService){
               this.formatter = formatterService;
        }
        public String getImageURL() {
            return FormatterService.getInstance().formatTachoIcon();
        }
    }
    
    and then test using Mockito, something like this.
    
    @Test
    public void testGetUrl(){
      FormatterService formatter = mock(FormatterService.class);
      when(formatter.formatTachoIcon()).thenReturn("TestURL");
      DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter);
      assertEquals(handler.getImageURL(), "TestUrl";
    }
    

    我只想完成noscreenname的解决方案。解决方案是使用PowerMockito。因为PowerMockito可以做类似于Mockito的事情,所以您可以使用PowerMockito

    示例代码如下所示:

    import org.junit.Test;
    导入org.junit.runner.RunWith;
    导入org.powermock.api.mockito.PowerMockito;
    导入org.powermock.core.classloader.annotations.PrepareForTest;
    导入org.powermock.modules.junit4.PowerMockRunner;
    导入java.lang.reflect.Field;
    导入静态org.powermock.api.mockito.PowerMockito.mock;
    导入静态org.powermock.api.mockito.PowerMockito.when;
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Singleton.class})
    公共类单音测试{
    @试验
    公共无效测试_1(){
    //创建一个模拟单例并进行更改
    Singleton mock=mock(Singleton.class);
    when(mock.dosth())。然后返回(“成功”);
    System.out.println(mock.dosth());
    //将该单例插入singleton.getInstance()中
    mockStatic(Singleton.class);
    when(Singleton.getInstance()).thenReturn(mock);
    System.out.println(“结果:+Singleton.getInstance().dosth());
    }
    }
    

    单身人士类别:

    公共类单例{
    私有静态单例实例;
    私人单身人士(){
    }
    公共静态单例getInstance(){
    if(实例==null){
    INSTANCE=newsingleton();
    }
    返回实例;
    }
    公共字符串dosth(){
    返回“失败”;
    }
    }
    
    这是我的毕业证书:

    /*
    *  version compatibility see: https://github.com/powermock/powermock/wiki/mockito
    *
    * */
    
    def powermock='2.0.2'
    def mockito='2.8.9'
    ...
    dependencies {
        testCompile group: 'junit', name: 'junit', version: '4.12'
    
        /** mock **/
        testCompile group: 'org.mockito', name: 'mockito-core', version: "${mockito}"
    
        testCompile "org.powermock:powermock-core:${powermock}"
        testCompile "org.powermock:powermock-module-junit4:${powermock}"
        testCompile "org.powermock:powermock-api-mockito2:${powermock}"
        /**End of power mock **/
    
    }
    

    在Mockito中,如果不使用PowerMock,就无法做到这一点,除非重构一个类。但我不知道你为什么要这么做。您正在用一行代码对一个方法进行单元测试,没有内部逻辑。这不能失败。测试的目的是确保clas sunder测试调用singletons方法。任何好的测试用例都不应该以这种方式为目的。相反,我们的目标是测试一些有意义的业务功能。模拟依赖项并验证调用了某个方法并不一定是错误的,但应该只在需要时进行。所有这些方法都会引起麻烦……它们适用于静态方法,但如果它是实例的方法,并且引用了类变量,它将获得空指针对吗?但powerMockito是一种方式。取而代之的是做我们以前做的事情,让我们的单例实现一个接口。然后在您的测试中使用接口,该接口中只有所有存根。参考:@fbielejec mock方法内部是什么?您需要在(FormatterService.getInstance())时删除行
    。然后返回(formatter)
    ,否则测试不会运行。第三种方法是认识到(可能)在不模拟si的情况下可以编写更好的测试