java 在单元测试中检查工厂的结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37310/
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-29 10:48:59  来源:igfitidea点击:

Checking the results of a Factory in a unit test

javaunit-testingtdd

提问by javelinBCD

I have developed some classes with similar behavior, they all implement the same interface. I implemented a factory that creates the appropriate object and returns the interface. I am writing a unit test for the factory. All you get back is an interface to the object. What is the best way to test that the factory has worked correctly?

我开发了一些具有相似行为的类,它们都实现了相同的接口。我实现了一个创建适当对象并返回接口的工厂。我正在为工厂编写单元测试。你得到的只是一个对象的接口。测试工厂是否正常工作的最佳方法是什么?

I would like to know the answer in Java, but if there is a solution that crosses languages I would like to know it.

我想知道 Java 中的答案,但如果有跨语言的解决方案,我想知道。

Number 2. in the answer, would be done like the other answer? If so I will mark the other answer accepted as well and reword my question to adress both a factory where an interface is returned and you have no clue what type of concrete class implemented the interface, and the case where you do know what concrete class was used.

答案中的第 2 点,会像其他答案一样吗?如果是这样,我也会将另一个答案标记为已接受,并改写我的问题以解决返回接口的工厂并且您不知道实现该接口的具体类的类型,以及您确实知道具体类是什么的情况用过的。

回答by Undreren

What you are trying to do is not Unit Testing

您要做的不是单元测试

If you test whether or not the returned objects are instances of specific concrete classes, you aren't unit testing. You are integration testing. While integration testing is important, it is not the same thing.

如果您测试返回的对象是否是特定具体类的实例,则不是单元测试。您正在集成测试。虽然集成测试很重要,但它不是一回事。

In unit testing, you only need to test the object itself. If you assert on the concrete type of the abstract objects returned, you are testing over the implementation of the returned object.

在单元测试中,您只需要测试对象本身。如果您对返回的抽象对象的具体类型进行断言,则您正在测试返回对象的实现。

Unit Testing on Objects in general

一般对象的单元测试

When unit testing, there are four things, you want to assert:

单元测试时,有四件事要断言:

  1. Return values of queries (non-void methods) are what you expect them to be.
  2. Side-effects of commands (void methods) modify the object itself as you expect them to.
  3. Commands send to other objects are received (This is usually done using mocks).
  1. 查询的返回值(非 void 方法)是您所期望的。
  2. 命令的副作用(void 方法)会按照您的预期修改对象本身。
  3. 接收发送到其他对象的命令(这通常使用模拟完成)。

Furthermore, you only want to test what could be observed from an object instance, i.e. the public interface. Otherwise, you tie yourself to a specific set of implementation details. This would require you to change your tests when those details change.

此外,您只想测试可以从对象实例(即公共接口)观察到的内容。否则,您将自己绑定到一组特定的实现细节。当这些细节发生变化时,这将要求您更改测试。

Unit Testing Factories

单元测试工厂

Unit testing on Factories is really uninteresting, because you are not interested in the behavior of the returned objects of queries. That behavior is (hopefully) tested elswhere, presumable while unit testing that object itself. You are only really interested in whether or not the returned object has the correct type, which is guaranteed if your program compiles.

对工厂进行单元测试真的很无趣,因为您对查询返回对象的行为不感兴趣。该行为(希望)在其他地方进行了测试,推测是在对该对象本身进行单元测试时。您只对返回的对象是否具有正确的类型真正感兴趣,这在您的程序编译时得到保证。

As Factories do not change over time (because then they would be "Builders", which is another pattern), there are no commands to test.

由于工厂不会随着时间的推移而改变(因为它们将成为“建造者”,这是另一种模式),因此没有要测试的命令。

Factories are responsible for instantiating objects, so they should not depend on other factories to do this for them. They mightdepend on a Builder, but even so, we are not supposed to test the Builder's correctness, only whether or not the Builder receives the message.

工厂负责实例化对象,所以他们不应该依赖其他工厂为他们做这件事。它们可能依赖于 Builder,但即便如此,我们也不应该测试 Builder 的正确性,只测试 Builder 是否收到消息。

This means that all you have to test on Factories is whether or not they send the messages to the objects on which they depend. If you use Dependency Injection, this is almost trivial. Just mock the dependencies in your unit tests, and verify that they receive the messages.

这意味着您必须在工厂上测试它们是否将消息发送到它们所依赖的对象。如果您使用依赖注入,这几乎是微不足道的。只需模拟单元测试中的依赖项,并验证它们是否收到消息。

Summary of Unit Testing Factories

单元测试工厂总结

  1. Do not test the behavior nor the implementation details of the returned objects! Your Factory is not responsible for the implementation of the object instances!
  2. Test whether or not the commands sent to dependencies are received.
  1. 不要测试返回对象的行为或实现细节!您的工厂不负责对象实例的实现!
  2. 测试是否接收到发送给依赖项的命令。

That's it. If there are no dependencies, there is nothing to test. Except maybe to assert that the returned object isn't a nullreference.

而已。如果没有依赖项,则无需测试。除了可能断言返回的对象不是null引用。

Integration Testing Factories

集成测试工厂

If you have a requirement that the returned abstract object type is an instance of a specific concrete type, then this falls under integration testing.

如果您要求返回的抽象对象类型是特定具体类型的实例,则这属于集成测试。

Others here have already answered how to do this using the instanceofoperator.

这里的其他人已经回答了如何使用instanceof操作符来做到这一点。

回答by Cem Catikkas

Since I don't know how your factory method looks like, all I can advise right now is to

由于我不知道你的工厂方法是什么样的,我现在只能建议

  1. Check to see the object is the correct concrete implementation you were looking for:

    IMyInterface fromFactory = factory.create(...);  
    Assert.assertTrue(fromFactory instanceof MyInterfaceImpl1);
    
  2. You can check if the factory setup the concrete instances with valid instance variables.

  1. 检查对象是否是您正在寻找的正确具体实现:

    IMyInterface fromFactory = factory.create(...);  
    Assert.assertTrue(fromFactory instanceof MyInterfaceImpl1);
    
  2. 您可以检查工厂是否使用有效的实例变量设置了具体实例。

回答by izb

if (myNewObject instanceof CorrectClass)
{
    /* pass test */
}

update:

更新:

Don't know why this got marked down, so I'll expand it a bit...

不知道为什么这被标记下来,所以我会扩大一点......

public void doTest()
{
    MyInterface inst = MyFactory.createAppropriateObject();
    if (! inst instanceof ExpectedConcreteClass)
    {
        /* FAIL */
    }
}

回答by Marcio Aguiar

@cem-catikkas I think it would be more correct to compare the getClass().getName() values. In the case that MyInterfaceImpl1 class is subclassed your test could be broken, as the subclass is instanceof MyInterfaceImpl1. I would rewrite as follow:

@cem-catikkas 我认为比较 getClass().getName() 值会更正确。在 MyInterfaceImpl1 类是子类的情况下,您的测试可能会被破坏,因为子类是 instanceof MyInterfaceImpl1。我会改写如下:

IMyInterface fromFactory = factory.create(...);  
Assert.assertEquals(fromFactory.getClass().getName(), MyInterfaceImpl1.class.getName());

If you think this could fail in some way (I can't imagine), make the two verifications.

如果您认为这可能会以某种方式失败(我无法想象),请进行两次验证。

回答by Federico Gatti

If your Factory returns a concrete instance you can use the @Parametersannotation in order to obtain a more flexible automatic unit test.

如果您的工厂返回一个具体实例,您可以使用@Parameters注释以获得更灵活的自动单元测试。

package it.sorintlab.pxrm.proposition.model.factory.task;

import org.junit.Test;

import java.util.Arrays;
import java.util.Collection;

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import static org.junit.Assert.*;

@RunWith(Parameterized.class)
public class TaskFactoryTest {

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { "sas:wp|repe" , WorkPackageAvailabilityFactory.class},
                { "sas:wp|people", WorkPackagePeopleFactory.class},
                { "edu:wp|course", WorkPackageCourseFactory.class},
                { "edu:wp|module", WorkPackageModuleFactory.class},
                { "else", AttachmentTaskDetailFactory.class}
        });
    }

    private String fInput;
    private Class<? extends TaskFactory> fExpected;

    public TaskFactoryTest(String input, Class<? extends TaskFactory> expected) {
        this.fInput = input;
        this.fExpected = expected;
    }

    @Test
    public void getFactory() {
        assertEquals(fExpected, TaskFactory.getFactory(fInput).getClass());
    }
}

This example is made using Junit4. You can notice that with only one line of code you can test all the result of your Factory method.

这个例子是使用Junit4 制作的。您会注意到,只需一行代码就可以测试工厂方法的所有结果。