Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/394.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 在方法级别隔离单元测试和存根内部方法调用是否正确?_Java_Unit Testing_Junit_Mockito - Fatal编程技术网

Java 在方法级别隔离单元测试和存根内部方法调用是否正确?

Java 在方法级别隔离单元测试和存根内部方法调用是否正确?,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,我总是怀疑单元测试隔离是否应该在非常精细的粒度上进行,比如存根内部方法调用,看看下面的Java、JUnit和Mockito示例 package com.company.domain; public class Foo { private FooHelper helper; public setHelper(FooHelper helper) { this.helper = helper; } public Double method1(In

我总是怀疑单元测试隔离是否应该在非常精细的粒度上进行,比如存根内部方法调用,看看下面的Java、JUnit和Mockito示例

package com.company.domain;

public class Foo {
    private FooHelper helper; 

    public setHelper(FooHelper helper) {
        this.helper = helper;
    }

    public Double method1(Integer a, Integer b) {
        ... // logic here
        Integer var = method2(a);  // internal call to method2
        ... // more logic here
    }

    protected Integer method2(Integer c) {
        ... // logic here and return an Integer
    }
}
这是测试类:

package com.company.domain;

@RunWith(MockitoJUnitRunner.class)    
public class FooTest {
    @Mock private FooHelper fooHelperMock; // mocking dependencies as usual
    @InjectMocks @Spy Foo foo; // this way we can stub internal calls

    @Test
    public void method1Test() {
        doReturn(2).when(foo).method2(1); //stub the internal method call
        ... // stub fooHelperMock method calls here

        Double result = foo.method1(1, 2);

        assertEquals(new Double(1.54), result);
        verify(foo, times(1)).method2(1);
        ... // verify fooHelperMock method calls here
    }

    @Test
    public void method2Test() {
        ... // stub fooHelper method calls here
        assertEquals(new Integer(5), foo.method2(3));
        ... // verify fooHelperMock method calls here
    }
}

我认为,通过这种方式,即使使用内部调用,您也能够在方法级别真正隔离正在测试的代码。但是我找不到很多关于这是否被认为是一个好的实践以及为什么这样做的信息。

一般来说,测试类的内部函数调用会使测试过于脆弱。当进行单元测试时,您只希望看到类作为一个整体正在做它应该做的事情。你应该故意忽略内部

忽略内部有很多有用的好处。主要是,如果您更改了方法——只要公共接口保持不变——您的测试仍然会通过。耶。这是您希望单元测试具有的稳定性类型。脆弱的单元测试几乎比没有单元测试更糟糕。每次代码更改时都必须更改脆弱的测试,即使一切仍然正常。那只会给你带来额外的工作

不过也有例外。在下面的例子中,我会考虑对内部函数调用进行嘲弄:

1) 内部调用从某处提取数据。例如,数据库或大型文件。这通常被称为“边界”测试。我们不想真正地设置一个数据库或文件来进行测试,所以我们只是将函数分出来。然而,如果你发现自己在这样做,这可能意味着你的类应该分为两个类,因为它做了太多不同的事情。让自己成为一个专用的数据库类

2) 内部调用需要大量处理,这需要花费大量时间。在这种情况下,将函数分出来而不是等待可能是有意义的。然而,这可能再次表明你走错了方向。您应该尝试找到一种方法,使事情更加分区,这样就不需要花那么长的时间

总之,我强烈建议您不要在方法级别进行测试。类应该是一个独立的单元。如果全班都在学习,一切都很好。这种方法为您提供了安全所需的测试,以及更改内容所需的灵活性

@拉斯帕科普

事实上,如果您只测试一个用例,那么有人可以硬编码var=2,然后通过您的测试用例。但是,您的工作是设计测试用例,以涵盖足够多的不同用例,从而合理地满足它的行为是适当的

至于测试因为代码更改而更改,这根本不是您想要的。您希望您的测试验证您得到的响应对于所有不同类型的情况都是正确的。只要回答正确,您就希望能够在根本不更改测试的情况下更改所有内容


想象一下,一个有数千个测试的大型系统验证一切正常。现在想象一下,您想要进行一次巨大的优化更改,这将使事情进展得更快,但要更改所有内容的存储和计算方式。您希望您的测试允许您做的只是更改代码(而不是测试),并让测试不断确认一切仍在工作。这就是单元测试的目的,不是捕捉每一个可能的行更改,而是验证所有的计算和行为是否正确。

我认为没有必要,我甚至想进一步说,您不应该这样做。您正在编写更多的测试代码来测试更少的应用程序代码。一般来说,只测试公共方法被认为是良好的实践,因为单元测试应该只关注被测试的方法是否满足其契约(即做它应该做的事情),而不关注该方法是如何实现的。如果你的私有方法中有一个bug,你怎么知道呢

这回答了你的问题

你和我一样是一个嘲弄者,但有很多人不喜欢这种方法,而更喜欢古典主义方法(例如肯特·贝克)

当测试失败时,测试的目标是识别导致问题的生产代码,记住这一点很明显,粒度越细越好


诚然,对于初学者来说,成为一名模仿者可能会让人不知所措,但一旦你习惯了模仿库的机制,好处是数不清的,而付出的努力也不会比其他方法高得多。

谢谢你的快速回复@Damien,您提到这种方法会使测试变得脆弱,因为每次代码更改时都必须更改测试。但这不是相反吗?我的意思是,一个强大的测试应该是详尽的,因为代码更改涉及测试代码更改以覆盖它?例如,在上面的示例中,如果有人来更改行:Integer var=method2(a);并将其硬编码为整数var=2;然后,在一个不验证内部调用的场景中,您将无法捕获它,因为它将产生相同的预期结果,当前的测试确实如此。Thx@mmilleruva,实际上示例中没有私有方法,我想您指的是受保护的方法,但是,这一个有自己的测试,所以如果有任何错误,他们将在那里捕获。您提到单元测试应该只关注满足契约,但有时单元测试更关注良好的代码覆盖率,例如确保它覆盖所有可能的分支、特殊条件、错误处理等。