java - 封装和模拟

标签 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));
    }
}

最佳答案

这实际上取决于您正在测试的逻辑。由于您的示例没有提供任何上下文,我会给您一个案例,如果我觉得不仅可以轻松测试这种交互,而且甚至是强制性的:

假设您正在测试身份验证 token 验证。您将一些 token 传递给您的 validator ,它返回 true/false。在您的 validator 中,您正在调用一些 jwt.validate 或任何其他第 3 方哈希验证方法。在这种情况下,我需要知道每次都会调用此 validator ,因为我可以在其中引入一些 if token == null 条件,它会绕过此验证调用并只返回 false。然后您的测试仍然可以通过,但您的代码现在容易受到时序攻击。

这是一种例子。另一种我喜欢用这种方式进行测试的测试类型称为 border testing。我想知道我的类(class)会触发 strip 支付网关 - 所以我模拟它并确保它被调用而不检查此特定测试中的任何复杂内容。

关于java - 封装和模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46080150/

相关文章:

java - 无法覆盖默认id列spring数据JPA

java - 如何克隆输入流但仍然重新使用原始流

java - Android Java - 如何从 URL 下载 zip 文件?

testing - SoapUI 测试 : remove quotation marks from session ID

android - UiAutomator 选择应用程序以从应用程序抽屉中进行测试

java - 在java中运行c定时器执行文件

typescript - 如何在 Typescript 中编写单元测试?

java - TestNG - 传递包含所有测试输入数据的 CSV 文件

python - 在 Python Flask 蓝图中模拟函数

javascript - 简单普通 JavaScript 的开 Jest 测试 - 无法读取 null 的属性 'addEventListener'