简体   繁体   English

用许多模拟对象创建测试?

[英]Creating tests with many mock objects?

I'm having doubts about if I should create tests that have many mock objects or not. 我对是否应该创建包含许多模拟对象的测试感到怀疑。

I recently read When should I mock? 我最近读了我什么时候应该嘲笑? and I'm feeling confused. 我感到困惑。

Let's take a look at a method I have (it is just to illustrate the problem) 让我们看一下我拥有的一种方法(只是为了说明问题)

@Override
protected void validate() throws WTException {
    Either<ImportError, RootFinderResult> rootPart = getDataValidator().getRootPart();
    if (rootPart.isLeft()) {
        addValidationMessage(ROOT_PART_NOT_FOUND);
    } else if (rootPart.isRight()) {
        getObjectsToValidate().forEach(Lambda.uncheckedBiConsumer((part, epmDocuments) -> {
            LocalizableMessage rootRevision = getRevision(part);

            Optional<EPMDocument> wrongRevisionEPM = epmDocuments.stream()
                    .filter(epmDocument -> !isSameRevision(rootRevision, epmDocument))
                    .findAny();

            wrongRevisionEPM.ifPresent(epmDocument -> addValidationMessage("blabla"));
        }));
    }
}

All of the following methods need to have a connection to a server in order to work, otherwise they will throw errors 以下所有方法都需要与服务器建立连接才能起作用,否则它们将引发错误

getDataValidator().getRootPart();
getRevision(part)
!isSameRevision(rootRevision, epmDocument))

In addition I can't create 'real' objects of part or epm documents. 另外,我无法创建零件或epm文档的“真实”对象。 This also requires to have a connection to a server. 这也需要与服务器建立连接。


So at this point, what I really want to test is actually logic of this part of code 所以在这一点上,我真正要测试的实际上是这部分代码的逻辑

    Optional<EPMDocument> wrongRevisionEPM = epmDocuments.stream()
            .filter(epmDocument -> !isSameRevision(rootRevision, epmDocument))
            .findAny();

    wrongRevisionEPM.ifPresent(epmDocument -> addValidationMessage("blabla"));

But to test it I need to mock really many objects 但是要测试它,我需要模拟很多对象

@Spy
@InjectMocks
private SameRevision sameRevision;
@Mock
private WTPartRelatedObjectDataValidator wTPartRelatedObjectDataValidator;
@Mock
private ValidationEntry validationEntry;
@Mock
private WTPart rootPart1, rootPart2;
@Mock
private EPMDocument epmDocument1, epmDocument2, epmDocument3;
@Mock
private Either<ImportError, RootFinderResult> rootPart;
@Mock
private LocalizableMessage rootPartRevisionOne, rootPartRevisionTwo;

so finally I can test the logic: 所以最后我可以测试逻辑:

@Test
@DisplayName("Should contain error message when part -> epms revisions are not the same")
void shoulHaveErrorMessagesWhenDifferentRevisions() throws Exception {
    doReturn(getMockObjectsToValidate()).when(sameRevision).getObjectsToValidate();

    doReturn(rootPart).when(liebherrWTPartRelatedObjectDataValidator).getRootPart();
    doReturn(false).when(rootPart).isLeft();
    doReturn(true).when(rootPart).isRight();

    doReturn(rootPartRevisionOne).when(sameRevision).getRevision(rootPart1);
    doReturn(rootPartRevisionTwo).when(sameRevision).getRevision(rootPart2);

    doReturn(true).when(sameRevision).isSameRevision(rootPartRevisionOne, epmDocument1);
    doReturn(false).when(sameRevision).isSameRevision(rootPartRevisionOne, epmDocument2);
    doReturn(true).when(sameRevision).isSameRevision(rootPartRevisionTwo, epmDocument3);

    validationEntry = sameRevision.call();

    assertEquals(1, validationEntry.getValidationMessageSet().size());
}

where 哪里

    doReturn(rootPart).when(liebherrWTPartRelatedObjectDataValidator).getRootPart();
    doReturn(false).when(rootPart).isLeft();
    doReturn(true).when(rootPart).isRight();

    doReturn(rootPartRevisionOne).when(sameRevision).getRevision(rootPart1);
    doReturn(rootPartRevisionTwo).when(sameRevision).getRevision(rootPart2);

can be moved to @BeforeEach. 可以移到@BeforeEach。


At last, I have my test and it works. 最后,我进行了测试,它可以正常工作。 It validates what I wanted to be validated but in order to come to this point I had to put a lot of effort to come through the whole API which needs interactions with a server. 它验证了我想要验证的内容,但是为了达到这一点,我不得不付出很多努力才能通过需要与服务器交互的整个API。

What do you guys think, is it worth it to create tests like this? 你们怎么看,创建这样的测试值得吗? I guess this is a wide-open topic 'cause many newbies that try to come into the 'test world' will have similar a problem, so please do not close the topic because of opinion-based judgement and give your feedback on this topic. 我想这是一个开放的话题,“因为许多尝试进入“测试世界”的新手都会遇到类似的问题,因此,请不要因为基于观点的判断而关闭该话题,并提供您对该话题的反馈。

You should mock other dependencies on which your class which going to be tested relies and set up behavior which you need . 您应该模拟将要测试的类所依赖的其他依赖关系,并设置所需的行为。 This needs to be done to test your method isolated and not dependent on thrirdparty classes You can write private void methods which can contain your mock behavior and use them in tests , In @BeforeEach annotated method you could mock behavior which wil be same in all tests or mock the same mocking behavior for across all tests 这需要完成以测试您的方法是否孤立,并且不依赖于thrirdparty类。您可以编写私有的void方法,该方法可以包含模拟行为,并在测试中使用它们;在@BeforeEach带注释的方法中,您可以模拟在所有测试中都相同的行为或在所有测试中模拟相同的模拟行为

In your method which is void you can have spy objects which can be veereified if they were called like Mockito.verify() 在无效的方法中,您可以拥有一些间谍对象,如果像Mockito.verify()这样调用它们,则可以对其进行仿制。

You are right. 你是对的。 It is a big effort to mock all those dependencies. 模拟所有这些依赖关系是一项巨大的努力。 Let me get over a few points that may make things clearer: 让我谈谈一些可能使事情变得更清楚的点:

  • Treat writing tests like an investment: So yes, sometimes it is more effort to write the test than to write the actual code. 像对待投资一样对待编写测试:因此,是的,有时编写测试要比编写实际代码花费更多的精力。 However, you will thank yourself later when you introduce a bug and the tests can catch it. 但是,稍后引入错误时,您将感激不尽,并且测试可以抓住它。 Having good tests gives you confidence when modifying your code that you didn't break anything, and if you did, your tests will find the issue. 拥有良好的测试可以使您有信心在修改代码时不会破坏任何内容,如果这样做,则测试会找到问题所在。 It pays off over time. 随着时间的流逝,它会得到回报。

  • Keep your test focused on a specific class. 让您的考试集中在特定的课程上。 Mock the rest: When you mock everything except the class under test, you can be sure that when a problem occurs that it is from the class under test, and not from one of its dependencies. 模拟其余部分:当模拟除被测类之外的所有内容时,可以确定发生问题时,该问题来自被测类,而不是其依赖项之一。 This makes troubleshooting a lot easier. 这使故障排除变得更加容易。

  • Think of testability when writing new code: Sometimes it may not be avoidable to have a complicated piece of code which is hard to test. 编写新代码时要考虑可测试性:有时候,不可避免地要编写复杂的代码,很难测试。 However, generally, this situation can be avoided by keeping the number of dependencies you have to a minimum and writing testable code. 但是,通常可以通过将所需的依赖项数量保持在最低限度并编写可测试的代码来避免这种情况。 For example, if a method needs 5 or 6 more dependencies to do its job, then probably that method is doing too much and could be broken down. 例如,如果一个方法需要5或6个以上的依赖关系来完成其工作,则该方法可能做得太多,可能会分解。 Same thing can be said on a class level, modules, etc.. 可以在类级别,模块等上说同样的话。

Yes, it is quite time investing when you have to mock so many things. 是的,当您必须模拟这么多东西时,是相当多的投资时间。 In my opinion, if you add some value when testing something, it is worth testing, the problem could be of course how much time would you consume. 我认为,如果您在测试某件产品时增加了一些价值,那值得进行测试,那么问题当然可能是您要花费多少时间。

In your specific case, I would test on different "layers". 在您的特定情况下,我将在不同的“层”上进行测试。

For example, the methods: getDataValidator().getRootPart(); 例如,方法:getDataValidator()。getRootPart(); getRevision(part) !isSameRevision(rootRevision, epmDocument)) getRevision(part)!isSameRevision(rootRevision,epmDocument))

They can be independently tested and in your case just mock their result, meaning that you don't really care about the parameters there, you just care what happens in case of a certain return value. 可以对它们进行独立测试,并在您的情况下仅模拟其结果,这意味着您实际上并不关心那里的参数,您只关心在有特定返回值的情况下会发生什么。

So, on one layer you really test the functionality, on the next layer you just mock the result you need, in order to test the other functionality. 因此,您可以在一层上真正地测试该功能,而在下一层上,您只需模拟所需的结果即可测试另一功能。

I hope it is more clear now... 我希望现在更清楚...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM