如何在JUnit中重用方法和测试?

时间:2017-08-07 14:26:23

标签: java junit mockito

我试图在JUnit测试中避免重复代码,但我有点卡住了。

这是我的第一个测试,对于第二个测试,它具有完全相同的方法但不同的服务(不同的输入)。而不是 TestCaseResourceTest1 我有 TestCaseResourceTest2 。现在测试两者的正确方法是什么?我想为测试编号2提供一个单独的文件,我应该如何避免重复的代码? (例如,使用beforeFileTest()方法)

public class TestCaseResourceTest1 {

    @Mock
    private TestService testService;
    @Mock
    private AreaService areaService;

    private TestCaseService1 testCaseService1; // is changed in test2

    @Before
    public void before() throws Exception{
        testCaseService1 = mock(TestCaseService1.class); // is changed in test2
        MockitoAnnotations.initMocks(this);
        beforeFileTest();
    }

    private void beforeFileTest() throws Exception{
        doReturn(true).when(areaService).chechExists(any(String.class), eq(false));
    }

    @Test
    public void verifyFileExists() throws Exception{
        verifyOtherArea(testCaseService1); // is changed in test2
        doReturn(false).when(areaService).chechExists(any(String.class), eq(false));
    }
}

注释is changed in test2只是差异。

TNX

5 个答案:

答案 0 :(得分:3)

假设您希望为2个不同的类进行完全相同的测试运行(而不是像示例代码中那样进行模拟),您可以创建一个抽象测试类,它具有返回类的实例的抽象方法测试

有些东西:

public abstract class TestCaseResourceTest {

  protected abstract TestCaseService1 getServiceToTest();

  @Before
  public void before() throws Exception {
    testCaseService1 = getServiceToTest();
    MockitoAnnotations.initMocks(this);
    beforeFileTest();
  }

  @Test
  public void test() {
    // do your test here
  }
}

public class ConcreteTest extends TestCaseResourceTest {
  protected abstract TestCaseService1 getServiceToTest() {
    return new TestCaseService();
  }
}

public class ConcreteTest2 extends TestCaseResourceTest {
  protected abstract TestCaseService1 getServiceToTest() {
    return new DifferentService();
  }
}

答案 1 :(得分:3)

我遇到了这种情况,这是错误的实施设计的标志。我们正在谈论纯单元测试,我们在那里测试生产类中实现的内容。如果我们需要重复测试,则意味着我们可能在实现中存在重复。

我是如何在项目中解决的?

  1. 将公共逻辑提取到父服务类中并为其实现单元测试。
  2. 对于子服务,我只为那里的特定实现代码实现了测试。没有了。
  3. 在真实环境中实施了集成测试,这些服务都涉及并完全测试。

答案 2 :(得分:2)

鉴于你的问题摘录:

  

...而不是TestCaseResourceTest1我有TestCaseResourceTest2 ...我想为测试编号2提供一个单独的文件

...在测试用例之间共享代码的标准方法是:

  • 创建Test Suite并在测试套件中包含共享代码(通常采用@BeforeClass@AfterClass方法)。这允许您(1)运行一次设置代码(每个套件调用); (2)封装共享设置/拆除代码,(3)以后轻松添加更多测试用例。例如:

    @RunWith(Suite.class)
    @Suite.SuiteClasses({
        TestCaseResourceTest1.class,
        TestCaseResourceTest2.class
    )}
    public class TestSuiteClass {
    
        @BeforeClass
        public void setup() {
            beforeFileTest();
        }
    
        private void beforeFileTest() throws Exception {
            // ...
        }
    }
    
  • 创建一个父类TestCaseResourceTest1TestCaseResourceTest2的抽象类,让这些测试用例调用父代的共享代码(通常通过super()调用)。使用此方法,您可以在父级中声明默认共享代码,同时仍允许子类(1)具有自己的行为,(2)有选择地覆盖父/默认行为

  • 创建自定义JUnit运行器,在此运行器中定义共享行为,然后使用@RunWith(YourCustomRunner.class)注释相关的测试用例。有关此方法的更多详细信息here

重申一些其他海报所说的话;这不是常见的第一步,因此如果您的使用提供了令人信服的理由,您可能更喜欢从简单开始,只迁移到套件或抽象类或自定义运行器。

答案 3 :(得分:0)

当从一个测试转到两个测试时,你不知道什么是重复代码,所以我发现将所有内容放入一个测试方法很有用。在这种情况下,首先将@Before和beforeFileTest方法的内容放入测试中。

然后您可以看到它只是需要更改的服务,因此您可以将除此之外的所有内容提取到从两个测试调用的辅助方法中。

此外,在您有两个调用相同辅助方法并且对该测试覆盖率感到满意的测试之后,您可以考虑编写参数化测试。例如,使用JunitParams:https://github.com/Pragmatists/junitparams/wiki/Quickstart

答案 4 :(得分:0)

您是否考虑过使用JUnit 5及其http://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests

它允许您使用不同的输入重复使用您的测试。这是文档中的一个示例,它说明了您现在可以使用JUnit 5执行的操作:

@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void testWithStringParameter(String argument) {
    assertNotNull(argument);
}

但您也可以创建返回输入数据的方法:

@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
    assertNotNull(argument);
}

static Stream<String> stringProvider() {
    return Stream.of("foo", "bar");
}

这里我只使用字符串,但你可以使用任何对象。

如果您使用的是Maven,则可以添加这些依赖项以开始使用JUnit 5:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.0.0-RC2</version>
    <scope>test</scope>
</dependency>

JUnit 5唯一令人讨厌的事情是它尚未发布。