封装和嘲弄

时间:2017-09-06 16:24:13

标签: java unit-testing testing mocking encapsulation

假设我有一个简单依赖的类:

public interface Dependency {
    int doSomething(int value);
    void doMore(int value);
    int doALotMore(int value);
}

public final class A implements SomeInterface {
    private final Dependency dep;

    public A (Dependency dep) {
        this.dep = dep;
    }

    @Override
    public int add(final int x) {
        dep.doMore(x);
        return x + dep.doSomething(x) + dep.doALotMore(x);
    }
}

我正在使用模拟编写测试:

public class TestA {
    private Dependency mockDep;
    private SomeInterface a;

    @Before
    public void setUp() {
        mockDep = Mockito.mock(Dependency.class);
        a = new A(mockDep);
    }

    @Test
    public void shouldAdd() {
        final int x = 5;
        when(mockDep.doSomething(x)).thenReturn(6);
        when(mockDep.doALotMore(x)).thenReturn(7);

        int actual = a.add(x);

        assertThat(actual, is(18));

        verify(mockDep, times(1)).doSomething();
        verify(mockDep, times(1)).doALotMore();
        verify(mockDep, times(1)).doMore();

        verifyNoMoreInteractions(mockDep);
    }
}

到目前为止一切顺利。

所以问题是:我们是否通过验证依赖关系的使用方式来违反类A的封装?是否真的需要测试依赖性是否以这种方式使用?我们不应该像测试用例一样测试A(删除verify调用,只留下assertThat)吗?在这种情况下如何处理依赖?

我问的原因是我发现自己编写了大量的验证依赖代码,似乎我们开始测试关于类的实际内部实现细节。我对此感到不舒服,因为当我尝试以另一种方式重写这个实现细节时,我需要重写测试用例,尽管例如add的结果将是相同的。如果我将我的类测试为黑盒子,我可以改变实现细节,并且仍然确保给定的输入将提供相同的输出。

或者有必要实际测试确切的实现细节,这是单元测试本身的重点?对我来说似乎有些不对。

请考虑这个测试:

 public class TestA {
    private Dependency mockDep;
    private SomeInterface a;
    private final int x = 5;

    @Before
    public void setUp() {
        mockDep = Mockito.mock(Dependency.class);
        a = new A(mockDep);

        when(mockDep.doSomething(x)).thenReturn(6);
        when(mockDep.doALotMore(x)).thenReturn(7);
    }

    @Test
    public void shouldAdd() {
        int actual = a.add(x);

        assertThat(actual, is(18));
    }
}

1 个答案:

答案 0 :(得分:3)

这实际上取决于您正在测试的逻辑。由于您的示例不提供任何背景信息,因此,当我感觉不仅可以测试此类互动时,我会给您一个案例,甚至是强制性的:

让我们说您正在测试身份验证令牌验证。您将一些令牌传递给验证器,它会返回let dateformatter = DateFormatter() dateformatter.dateStyle = .medium dateformatter.timeStyle = .short let dateFromString = dateformatter.date(from: selectDateTextField.text!) let fireDateOfNotification: Date = dateFromString! var trigger: UNCalendarNotificationTrigger var triggerDate: DateComponents var repeatInterval = Bool() if repeatingInterval == "Hourly" { triggerDate = Calendar.current.dateComponents([.minute], from: fireDateOfNotification) repeatInterval = true } else if repeatingInterval == "Daily" { triggerDate = Calendar.current.dateComponents([.hour, .minute], from: fireDateOfNotification) repeatInterval = true } else if repeatingInterval == "Weekly" { triggerDate = Calendar.current.dateComponents([.weekday, .hour, .minute], from: fireDateOfNotification) repeatInterval = true } else if repeatingInterval == "Monthly" { triggerDate = Calendar.current.dateComponents([.day, .hour, .minute], from: fireDateOfNotification) repeatInterval = true } else if repeatingInterval == "Yearly" { triggerDate = Calendar.current.dateComponents([.month, .day, .hour, .minute], from: fireDateOfNotification) repeatInterval = true } else { triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: fireDateOfNotification) repeatInterval = false } trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: repeatInterval) //Schedule the Notification ...... / true。在您的验证器内部,您正在调用某些false或任何其他第三方哈希验证方法。在这种情况下,我需要知道每次调用此验证器,因为我可以在其中引入一些jwt.validate条件,它将绕过此验证调用并返回false。然后你的测试仍然可以通过,但你的代码现在容易受到时间攻击的影响。

这是一个例子。我很容易通过这种方式测试的另一种类型的测试是if token == null。我想知道我的类触发条带支付网关 - 所以我嘲笑它并确保在没有检查这个特定测试中的任何复杂的情况下调用它。