如何在 1 个测试类中使用相同的方法测试多个对象? [英] How to test multiple objects with same method in 1 test class?

查看:34
本文介绍了如何在 1 个测试类中使用相同的方法测试多个对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个不同的对象:C c 和 B b.B 和 C 实现了接口 A,以便它们可以使用接口 A 中存在的称为 color() 的方法.我已经对类 B 进行了单元测试,以测试 B 已实现的 color() 方法.所以我现在想做的是用 B 类的相同单元测试测试 C 类中的 color() 方法.因此我想在我为 B 类制作的同一个测试类中测试它们.

I have 2 different objects: C c and B b. B and C implement interface A so that they can use method called color() that is present in interface A. I already made unit test for class B that test the color() method that B has implemented. So what I want to do now is test the color() method in class C with the same unit test of class B. Thus I want to test both of them in the same test class that I have made for class B.

为了实现这一点,我的一位朋友说我必须利用这些类的并行类层次结构.但我真的不知道我应该如何在我的测试中实现它.

To achieve this, one of my friends said that I would have to make use of the parallel class hierarchy of those classes. But I don't really know how I should implement that in my test.

这就是我所拥有的代码:

This is what I have in terms of code:

private static Sprites sprites;
private static B b;

@BeforeAll
static void setUp() {
    sprites = mock(Sprites.class);
    b = new B(sprites);
}

@Test
void testing_color_method() {

    M m = mock(M.class);

    b.color(m);

    verify(sprites).getSpritesOf(m);

}

//... some more tests

我正在使用 JUnit 5,我知道我可以使用 @ParameterizedTest 在测试中注入对象 B 和 C 以让它们使用相同的单元测试,我也做了一些谷歌搜索,但没有一个搜索结果有它是关于需要在 1 个测试中注入 2 个对象的这种情况.那么我应该如何重构代码,以便我可以注入 B 类和 C 类,让它们使用我已经为 B 编写的相同单元测试?

I am using JUnit 5 and I know I could use @ParameterizedTest to inject objects B and C in the test to let them use the same unit tests, I have also done some google search about this but none of the search results had it about this kinds of cases where 2 objects need to be injected in the 1 test. So how should I refactor the code so that I can inject class B and C to let them use the same unit tests that I already crated for B?

推荐答案

BC 使用相同的测试意味着,您实际上是通过接口进行测试 <代码>A.这些将主要是黑盒测试,因为您的测试只能依赖于接口的元素,而不能依赖于 BC 中特定实现的元素.更多详情请见下文.

Using the same tests for B and C means, you are in fact testing via the interface A. These would be then be primarily black-box tests, because your tests can only depend on elements of the interface, but not elements of the specific implementations in B and C. More about this below.

然而,首先,一些示例代码如何实现您的目标:该方法使用使用 @MethodSource 的 Junit 5 参数化测试.在示例中,@MethodSource 作为参数提供 a) 测试的描述(未在测试方法逻辑中使用,因此在那里称为 dummy),b)一个实现接口 A 的类的实例,并且,为了一个工作示例,您可以试验,c) 对象的预期类名.

First, however, some example code how you could achieve your goal: The approach uses the Junit 5 parameterized tests using @MethodSource. In the example, the @MethodSource provides as arguments a) a description of the test (which is not used in the test method logic and thus called dummy there), b) an instance of one of the classes which implement interface A, and, for the sake of a working example you can experiment with, c) the object's expected class name.

接口 A 的代码:

package so56386880;

public interface A {
    public void color();
}

类 B 和 C 的代码(我使用了相同的代码,此处显示的是 B):

Code for classes B and C (I used the same code, showing B here):

package so56386880;

public class B implements A {
    public void color() { }
}

测试代码

package so56386880;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

class A_Test {

    @DisplayName("Test one common property of color method")
    @ParameterizedTest(name = "{index}: {0}")
    @MethodSource("provideImplementors")
    void color_whenCalled_shallXxx(String dummy, A someA, String expectedClassName) {
        // you would do some test for someA.color(), but just for example:
        assertEquals(expectedClassName, someA.getClass().getName());
    }

    static Stream<Arguments> provideImplementors() {
        return Stream.of(
            Arguments.of("expecting class B's name", new B(), "so56386880.B"),
            Arguments.of("expecting class C's name", new C(), "so56386880.C"));
    }

}

如上所述,这只能执行仅限于使用接口 A 的测试.由于 BC 将有不同的实现,因此您可能还需要对 BC 进行一些特定的测试>.请记住,测试的目标是发现错误 - 不同的实现通常具有不同的潜在错误(不同的控制流、不同的溢出可能性等).

As said above, this can only perform tests that are restricted to using the interface A. Since B and C will have different implementations, it is likely that you also need some specific tests for B and C. Keep in mind that the goal of testing is to find bugs - and different implementations typically have different potential bugs (different control flows, different possibilities for overflows, etc.).

更新:上面的解决方案回答了如何使用参数化测试来实现,这些测试提供被测类作为参数.另一种方法是为接口实现一个测试类如下:

Update: The solution above answers how to do it with parameterized tests that provide the class under test as an argument. An alternative approach could be to implement a test class for the interface as below:

package so56386880;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

abstract class A_Test2 {

    public abstract A getClassUnderTest();

    @Test
    void color_whenCalled_shallXxx() {
        A classUnderTest = this.getClassUnderTest();
        classUnderTest.color(); // exercise
        assertEquals(expected..., actual...);
    }
}

然后从中派生,如下所示:

And then derive from it, like shown below:

package so56386880;

class B_Test extends A_Test2 {
    public A getClassUnderTest() { return new B(); }
}

在 Junit 5 下运行 B_Test 时,基类中的所有测试方法都会被执行(如果在 B_Test 中定义,还会执行额外的测试方法).优点是,无论何时创建新的派生类,您都不必修改 A_Test2.但是,如果每个派生类的预期值都不同(第一个解决方案中的类名就是这种情况),则需要进行一些调整,例如更多类似于 getClassUnderTest 的回调方法.

When running B_Test under Junit 5, all test methods in the base class get executed (plus additional ones if defined within B_Test). The advantage is, that you don't have to modify A_Test2 whenever a new derived class is created. If you have, however, expected values that differ for each derived class (as was the case with the class name in the first solution), this requires some adaption, for example more callback methods similar to getClassUnderTest.

这篇关于如何在 1 个测试类中使用相同的方法测试多个对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆