java 使用 Mockito 调用多个其他方法的方法的单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12625002/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 09:36:25  来源:igfitidea点击:

Unit test for method that calls multiple other methods using Mockito

javaunit-testingmockito

提问by aquacode

Perhaps I have completely fallen short in my search, but I cannot locate any documentation or discussions related to how to write a unit test for a Java class/method that in turn calls other non-private methods. Seemingly, Mockito takes the position that there is perhaps something wrong with the design (not truly OO) if a spy has to be used in order to test a method where mocking internal method calls is necessary. I'm not certain this is always true. But using a spy seems to be the only way to accomplish this. For example, why could you not have a "wrapper" style method that in turn relies on other methods for primitive functionality but additionally provides functionality, error handling, logging, or different branches dependent on results of the other methods, etc.?

也许我的搜索完全没有达到,但我找不到任何与如何为 Java 类/方法编写单元测试相关的文档或讨论,该类/方法又调用其他非私有方法。看起来,Mockito 认为如果必须使用间谍来测试需要模拟内部方法调用的方法,那么设计可能有问题(不是真正的面向对象)。我不确定这总是正确的。但是使用间谍似乎是实现这一目标的唯一方法。例如,为什么不能有一个“包装器”风格的方法,它反过来依赖于其他方法的原始功能,但另外提供功能、错误处理、日志记录或依赖于其他方法的结果的不同分支等?

So my question is two-fold:

所以我的问题有两个方面:

  1. Is it poorly designed and implemented code to have a method that internally calls other methods?
  2. What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?
  1. 具有内部调用其他方法的方法是否设计和实现不佳的代码?
  2. 如果选择 Mockito 作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法是什么(假设它本身是一个好主意)?

This might be a difficult request, but I would prefer for those who decide to answer to not merely re-publish the Mockito verbiage and/or stance on spies as I already am aware of that approach and ideology. Also, I've used Powermockito as well. To me, the issue here is that Mockito developed this framework where additional workarounds had to be created to support this need. So I suppose the question I am wanting an answer to is if spies are "bad", and Powermockito were not available, how is one supposed to unit test a method that calls other non-private methods?

这可能是一个困难的要求,但我更希望那些决定回答的人不仅仅是重新发布 Mockito 的措辞和/或对间谍的立场,因为我已经意识到这种方法和意识形态。另外,我也使用过 Powermockito。对我来说,这里的问题是 Mockito 开发了这个框架,必须创建额外的解决方法来支持这种需求。所以我想我想要回答的问题是如果间谍是“坏的”,并且 Powermockito 不可用,那么应该如何对调用其他非私有方法的方法进行单元测试?

回答by Eric

Is it poorly designed and implemented code to have a method that internally calls other methods?

具有内部调用其他方法的方法是否设计和实现不佳的代码?

Not really. But I'd say that, in this situation, the method that calls the others should be tested as if the others where not already tested separately. That is, it protects you from situations where your public methods stops calling the other ones without you noticing it.

并不真地。但是我想说的是,在这种情况下,应该测试调用其他方法的方法,就像其他尚未单独测试的方法一样。也就是说,它可以保护您免受公共方法在您没有注意到的情况下停止调用其他方法的情况。

Yes, it makes for (sometimes) a lot of test code. I believe that this is the point: the pain in writing the tests is a good clue that you might want to consider extracting those sub-methods into a separate class.

是的,它(有时)会产生大量的测试代码。我相信这就是重点:编写测试的痛苦是一个很好的线索,您可能想要考虑将这些子方法提取到一个单独的类中。

If I can live with those tests, then I consider that the sub-methods are not to be extracted yet.

如果我可以接受这些测试,那么我认为还没有提取子方法。

What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?

如果选择 Mockito 作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法是什么(假设它本身是一个好主意)?

I'd do something like that:

我会做这样的事情:

public class Blah {
    public int publicMethod() {
        return innerMethod();
    }

    int innerMethod() {
        return 0;
    }
}


public class BlahTest {
    @Test
    public void blah() throws Exception {
        Blah spy = spy(new Blah());
        doReturn(1).when(spy).innerMethod();

        assertThat(spy.publicMethod()).isEqualTo(1);
    }
}

回答by John Deverall

To me, this question relates strongly to the concept of cohesion.

对我来说,这个问题与凝聚力的概念密切相关。

My answer would be:

我的回答是:

It is ok to have methods (public) that call other methods (private) in a class, in fact very often that is what I think of as good code. There is a caveat to this however in that your class should still be strongly cohesive. To me that means the 'state' of your class should be well defined, and the methods (think behaviours) of your class should be involved in changing your classes state in predictable ways.

在类中拥有调用其他方法(私有)的方法(公共)是可以的,实际上这通常是我认为的好代码。然而,对此有一个警告,因为您的班级仍应具有很强的凝聚力。对我来说,这意味着你的类的“状态”应该被很好地定义,并且你的类的方法(思考行为)应该以可预测的方式改变你的类状态。

Is this the case with what you are trying to test? If not, you may be looking at one class when you should be looking at two (or more).

您尝试测试的内容是否属于这种情况?如果不是,您可能在看一门课,而你应该看两门(或更多)。

What are the state variables of the class you're trying to test?

您要测试的类的状态变量是什么?

You might find that after considering the answers to these types of questions, your code becomes much easier to test in the way you think it should be.

您可能会发现,在考虑了这些类型问题的答案之后,您的代码变得更容易以您认为应该的方式进行测试。

回答by Will Marcouiller

The real question should be:

真正的问题应该是:

What do I really want to test?

我真正想测试什么?

And actually the answer should be:

实际上答案应该是:

The behaviour of my object in response to outside changes

我的对象响应外部变化的行为

That is, depending on the way one can interact with your object, you want to test every possible single scenario in a single test. This way, you can make sure that your class reacts according to your expectations depending on the scenario you're providing your test with.

也就是说,根据与对象交互的方式,您希望在单个测试中测试每个可能的单个场景。这样,您可以确保您的类根据您提供测试的场景根据您的期望做出反应。

Is it poorly designed and implemented code to have a method that internally calls other methods?

具有内部调用其他方法的方法是否设计和实现不佳的代码?

Not really, and really not! These so called private methods that are called from public members are namely helper methods. It is totally correct to have helper methods!

不是真的,真的不是!这些从公共成员调用的所谓私有方法就是辅助方法。有辅助方法是完全正确的!

Helper methods are there to help break some more complex behaviours into smaller pieces of reusable code from within the class itself. Only it knows how it should behave and return the state accordingly through the public members of your class.

辅助方法用于帮助将一些更复杂的行为从类本身内部分解为更小的可重用代码。只有它知道它应该如何表现并通过您班级的公共成员相应地返回状态。

It is unrare to see a class with helper methods and normally they are necessary to adopt an internal behaviour for which the class shouldn't react from the outside world.

看到具有辅助方法的类并不罕见,通常它们必须采用内部行为,类不应从外部世界做出反应。

What is the best practice and/or approach in writing a unit test for such a method (assuming it is itself a good idea) if one has chosen Mockito as their mocking framework?

如果选择 Mockito 作为他们的模拟框架,那么为这种方法编写单元测试的最佳实践和/或方法是什么(假设它本身是一个好主意)?

In my humble opinion, you don't test those methods. They get tested when the public members are tested through the state that you expect out of your object upon a public member call. For example, using the MVP pattern, if you want to test user authentication, you shall not test every private methods, since private methods might as well call other public methods from an object on which depend the object under test and so forth. Instead, testing your view:

以我的拙见,您不会测试这些方法。当公共成员通过您在公共成员调用时期望的对象状态进行测试时,它们会得到测试。例如,使用MVP模式,如果要测试用户身份验证,则不应测试每个私有方法,因为私有方法也可能从依赖于被测对象的对象调用其他公共方法等等。相反,测试您的视图:

@TestFixture
public class TestView {
    @Test
    public void test() {
        // arrange
        string expected = "Invalid login or password";
        string login = "SomeLogin";
        string password = "SomePassword";

        // act
        viewUnderTest.Connect(login, password);
        string actual = viewUnderTest.getErrorMessage;

        // assert
        assertEqual(expected, actual);
    }
}

This test method describes the expected behaviour of your view once the, let's say, connectButton is clicked. If the ErrorMessageproperty doesn't contain the expected value, this means that either your view or presenter doesn't behave as expected. You might check whether the presenter subscribed to your view's Connectevent, or if your presenter sets the right error message, etc.

这个测试方法描述了你的视图的预期行为,比如说,connectButton 被点击。如果该ErrorMessage属性不包含预期值,这意味着您的视图或演示者的行为不符合预期。您可以检查演示者是否订阅了您的视图Connect事件,或者您的演示者是否设置了正确的错误消息等。

The fact is that you never need to test whatever is going on in your private methods, as you shall adjust and bring corrections on debug, which in turn causes you to test the behaviour of your internal methods simultaneously, but no special test method should be written expressly for those helper method.

事实是,您永远不需要测试私有方法中发生的任何事情,因为您将在调试时进行调整和修正,这反过来又会导致您同时测试内部方法的行为,但不应有特殊的测试方法专门为那些辅助方法编写的。

回答by jalynn2

If you really need (or want) to avoid calling the lower-level methods again, you can stub them out instead of mocking them. For example, if method A calls B and C, you can do this:

如果你真的需要(或想要)避免再次调用低级方法,你可以将它们存根而不是模拟它们。例如,如果方法 A 调用 B 和 C,您可以这样做:

MyClass classUnderTest = new MyClass() {
    @Override
    public boolean B() {return true;}

    @Override
    public int C() {return 0;}
};
doOtherCommonSetUp(classUnderTest);
String result = classUnderTest.A("whatever");
assertEquals("whatIWant", result);

I've used this quite a quite a bit with legacy code where extensive refactoring could easily lead to the software version of shipwright's disease: Isolate something difficult to test into a small method, and then stub that out.

我已经在遗留代码中使用了很多,在这些遗留代码中,大量重构很容易导致软件版本的造船工人病:将一些难以测试的东西隔离到一个小方法中,然后将其存根。

But if the methods being called are fairly innocuous and don't requiring mocking, I just let them be called again without worrying that I am covering every path within them.

但是,如果被调用的方法相当无害并且不需要模拟,我只是让它们再次被调用而不必担心我会覆盖其中的每条路径。