2016-08-12 69 views
8

我需要测试一些遗留代码,它在方法调用中使用单身人士。测试的目的是确保clas sunder测试调用singletons方法。 我已经看到过类似的问题,但所有的答案都需要其他的依赖(不同的测试框架) - 不幸的是我仅限于使用Mockito和JUnit,但这种流行的框架应该是完全可能的。用mockito嘲笑一个单身人士

单例:

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(); 

    } 

} 

当时的想法是配置可怕的单身人士的预期行为,因为被测试的类将调用它的getInstance然后formatTachoIcon方法。不幸的是,这会失败,并显示错误消息:

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

你不能做到这一点的Mockito中,也没有使用PowerMock,除非你重构你的类之一。但我不确定你为什么想要。你只用一行来单元测试一个方法,而没有内部逻辑。这不能失败。在别处花费您的测试工作。 –

+0

“测试的目的是确保clas sunder测试调用singletons方法。”没有好的测试案例应该有这样的事情作为它的目的。相反,目标是测试一些有意义的业务功能。嘲笑依赖关系并验证方法被调用不一定是错误的,但它应该只在需要的时候完成。 –

回答

9

你所问,因为你的遗留代码依赖于一个静态方法getInstance()和的Mockito不允许嘲笑静态方法,所以下面一行将无法正常工作

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

有2种方式是不可能的围绕这个问题:

  1. 使用不同的模拟工具,如PowerMock,允许模拟静态方法。

  2. 重构您的代码,以便您不依赖于静态方法。我能想到的最小侵入性方式是通过向DriverSnapshotHandler添加一个构造函数来注入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(); 
+0

第三种方法是识别)可以在不嘲笑单例的情况下编写更好的测试。在这种情况下我们无法确定,因为它是[XY问题](http://xyproblem.info)。 –

0

您的getInstance方法是静态的,因此不能使用mockito进行模拟。 http://cube-drone.com/media/optimized/172.png。您可能想要使用PowerMockito这样做。 Allthough我不会推荐这样做。我会通过依赖注入测试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(formatter.formatTachoIcon()).thenReturn("MockedURL"); 

    } 

    @Test 
    public void testFormatterServiceIsCalled() { 

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

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

    } 

} 

您可能需要设置模拟到空的@After方法。 这是我的更清洁的解决方案。

+1

您需要删除'when(FormatterService.getInstance())。然后返回(格式化程序)'行,否则测试将不会运行 – noscreenname

+0

当然 - 这是一个错字; o) –