jUnit中的多个RunWith语句


113

我写单元测试,并希望使用JUnitParamsRunnerMockitoJUnitRunner一个测试类。

不幸的是,以下操作无效:

@RunWith(MockitoJUnitRunner.class)
@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {
  // some tests
}

有没有一种方法可以在一个测试类中同时使用Mockito和JUnitParams?



2
这里也有一个很好的例子:blog.project13.pl/index.php/coding/1077/…–
falsarella

Answers:


110

您不能执行此操作,因为根据规范,您不能将相同的注释两次放在相同的注释元素上。

那么,解决方案是什么?解决的办法是只放一个@RunWith()你无法忍受的赛跑者,然后用其他东西代替。对于您的情况,我想您将删除MockitoJUnitRunner并以编程方式执行此操作。

实际上,它唯一要做的就是运行:

MockitoAnnotations.initMocks(test);

在测试用例的开始。因此,最简单的解决方案是将以下代码放入setUp()方法中:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

我不确定,但是您可能应该避免使用flag多次调用此方法:

private boolean mockInitialized = false;
@Before
public void setUp() {
    if (!mockInitialized) {
        MockitoAnnotations.initMocks(this);
        mockInitialized = true;  
    }
}

但是,可以使用JUnt的规则实现更好的可重用解决方案。

public class MockitoRule extends TestWatcher {
    private boolean mockInitialized = false;

    @Override
    protected void starting(Description d) {
        if (!mockInitialized) {
            MockitoAnnotations.initMocks(this);
            mockInitialized = true;  
        }
    }
}

现在,只需将以下行添加到您的测试类中:

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

您可以与任何所需的运行程序一起运行此测试用例。


12
检查mockInitialized是否错误。您想为每一次测试准备一个新鲜的模拟。
BetaRide 2015年

1
@BetaRide,这取决于您的需求。有时您希望每次都初始化模拟,有时则不需要。
AlexR

如果要为每个类文件设置一次,则可以使用BeforeClass而不是Before,它将被调用一次,并且每个测试文件仅被调用一次。
InfernalRapture

56

从JUnit 4.7和Mockito 1.10.17开始,此功能是内置的。有一个org.mockito.junit.MockitoRule课。您可以简单地将其导入并添加行

@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

参加您的测试班。


2
对于旧版本的Mockito(似乎降至1.10.5),您必须使用:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun

MockitoAnnotations.initMocks(this)创建模拟非常慢。最有效的方法是使用@Runwith(MockitoJunitRunner.class)
ant2009'9

16

该解决方案适用于所有可能的跑步者,而不仅是这个模仿示例。例如; 对于Spring,只需更改亚军类并添加必要的注释。

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

    @Test
    public void subRunner() throws Exception {
        JUnitCore.runClasses(TestMockitoJUnitRunner.class);
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class TestMockitoJUnitRunner {
    }
}

DatabaseModelTest将由JUnit运行。TestMockitoJUnitRunner取决于它(通过逻辑),它将在调用过程中在main 内部@Test方法中运行JUnitCore.runClasses(TestMockitoJUnitRunner.class)。此方法可确保主运行程序在static class TestMockitoJUnitRunner子运行程序运行之前正确启动,从而有效地实现@RunWith具有相关测试类的多个嵌套注释。

也在https://bekce.github.io/junit-multiple-runwith-dependent-tests


3
JUnitCore.runClasses()如果不检查结果就进行调用,则可能会掩盖内部测试中的错误。assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());至少会向您报告错误
Robotnik '18年


2

就我而言,我试图在春豆中模拟一些方法,

MockitoAnnotations.initMocks(test);

不起作用。相反,您必须定义要在xml文件中使用模拟方法构造的bean,如下所示。

...
<bean id="classWantedToBeMocked" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.fullpath.ClassWantedToBeMocked" />
</bean>
...

并在测试类中添加自动装配的bean,如下所示。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="file:springconfig.xml")
public class TestClass {
    ...
    @Autowired
    private ClassWantedToBeMocked classWantedToBeMocked;
    ...
    when(classWantedToBeMocked.methodWantedToBeMocked()).thenReturn(...);
    ...
}


0

我想同时运行SWTBotJunit4ClassRunnerorg.junit.runners.Parameterized,我有参数测试,我想在SWT测试失败时截屏(截屏功能由SWTBotJunit4ClassRunner提供)。@bekce的答案很好,首先想走这条路线,但是通过参数传递古怪。或在子类中进行参数化,并丢失信息,确切的测试通过/失败了,并且只有最后一个屏幕截图(因为屏幕截图名称是从测试本身获取的名称)。所以无论哪种方式都有点混乱。

就我而言,SWTBotJunit4ClassRunner很简单,因此我克隆了该类的源代码,给它起了自己的名字ParametrizedScreenshotRunner,而原来是在扩展TestRunner的地方,我的类是在扩展Parameterized类,因此从本质上讲,我可以使用自己的运行器而不是前两个。归结起来,我自己的运行程序扩展到了参数化运行程序的顶部,同时实现了它的屏幕快照功能,现在我的测试使用此“混合”运行程序,并且所有测试都可以按预期工作(无需在测试内部进行任何更改)。

这是它的样子(为简洁起见,我从清单中删除了所有评论):

package mySwtTests;

import org.junit.runners.Parameterized;
import org.eclipse.swtbot.swt.finder.junit.ScreenshotCaptureListener;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;

public class ParametrizedScreenshotRunner extends TestRu Parameterized {

    public ParametrizedScreenshotRunner(Class<?> klass) throws Throwable {
        super(klass);
    }

    public void run(RunNotifier notifier) {
        RunListener failureSpy = new ScreenshotCaptureListener();
        notifier.removeListener(failureSpy); // remove existing listeners that could be added by suite or class runners
        notifier.addListener(failureSpy);
        try {
            super.run(notifier);
        } finally {
            notifier.removeListener(failureSpy);
        }
    }
}

-15

您也可以尝试以下操作:

@RunWith(JUnitParamsRunner.class)
public class AbstractTestClass {
  // some tests
}

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}

2
这将不起作用,将仅处理子类注释。
PaulNUK,2015年

不起作用-仅考虑MockitoJUnitRunner注释
Przemek Bielicki 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.