jUnit中的多个RunWith语句

127

我编写单元测试并想在一个测试类中使用JUnitParamsRunnerMockitoJUnitRunner

不幸的是,以下方法不能正常工作:

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

有没有办法在一个测试类中同时使用Mockito和JUnitParams?


2
请看:https://code.google.com/p/junitparams/wiki/MergingWithOtherRunners - Alexey Gavrilov
2
这里还有一个不错的示例:http://www.blog.project13.pl/index.php/coding/1077/runwith-junit4-with-both-springjunit4classrunner-and-parameterized/ - falsarella
9个回答

125

你不能这样做,因为根据规范,你不能在同一个注释元素上放置相同的注释两次。

那么,有什么解决办法吗?答案是只使用一个带有你不能没有的运行器@RunWith(),并用其他东西替换另一个运行器。在你的情况下,我猜你会移除MockitoJUnitRunner,然后以编程方式完成它所做的事情。

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

MockitoAnnotations.initMocks(test);

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

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

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

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

然而,更好、可重复使用的解决方案可以通过JUnit规则实现。

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();

你可以选择任何测试运行器来运行这个测试案例。


15
mockInitializedзҡ„жЈҖжҹҘжҳҜй”ҷиҜҜзҡ„гҖӮдҪ еёҢжңӣжҜҸдёӘжөӢиҜ•йғҪжңүдёҖдёӘж–°зҡ„жЁЎжӢҹеҜ№иұЎгҖӮ - BetaRide
1
@BetaRide,这取决于您的需求。有时候您想每次都初始化模拟,有时则不需要。 - AlexR
1
如果您想在每个类文件中设置一次,可以使用BeforeClass而不是Before,它将在每个测试文件中被调用一次且仅一次。 - InfernalRapture

60

从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
但正如OP所提到的,您不能使用两个@RunWith语句,他需要使用另一个。 - Erica Kane

19

这个解决方案适用于所有可能的运行器,而不仅仅是这个Mockito示例。例如;对于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依赖于它(按逻辑),并且它将在@Test方法中的主要内容内部运行,同时调用JUnitCore.runClasses(TestMockitoJUnitRunner.class)。此方法确保在static class TestMockitoJUnitRunner子运行程序运行之前正确启动主运行程序,有效地实现了具有依赖测试类的多个嵌套@RunWith注释。

还可以访问https://bekce.github.io/junit-multiple-runwith-dependent-tests


4
如果在不检查结果的情况下调用JUnitCore.runClasses(),则有可能掩盖内部测试中的错误。assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful()); 至少会向您报告错误。 - Robotnik

8

2

在我的情况下,我正在尝试Mock Spring Bean中的一些方法。

MockitoAnnotations.initMocks(test);

不起作用。相反,您需要在xml文件中使用mock方法定义该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
虽然在 JUnit 4 中没有解决方案,但在 JUnit 5 中可以注册多个扩展。
@ExtendWith(MockitoExtension.class)
@ExtendWith(AnotherExtension.class)
public class MyTest {
  // some tests
}

请注意,JUnitParams框架已内置于JUnit 5中。

0

看看这个链接 https://bekce.github.io/junit-multiple-runwith-dependent-tests/,使用这种方法,我将 @RunWith(Parameterized.class) - 外部 runner - 与 @RunWith(MockitoJUnitRunner.class) - 内部 runner - 结合起来。唯一需要添加的调整是使外部类/runner 中的成员变量静态化,以便让内部/嵌套 runner/class 可以访问它们。祝好运并享受。


0

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

在我的情况下,SWTBotJunit4ClassRunner足够简单,因此我克隆了该类的源代码,给它起了自己的名字ParametrizedScreenshotRunner,原来的类扩展了TestRunner,而我的类则扩展了Parameterized类,因此实质上我可以使用自己的运行器代替前两个。简而言之,我的运行器在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
不起作用 - 只有 MockitoJUnitRunner 注释会被考虑。 - Przemek Bielicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接