【问题标题】:Calling a private method inside every JUnit before any test steps在任何测试步骤之前调用每个 JUnit 内部的私有方法
【发布时间】:2015-12-09 05:12:18
【问题描述】:

在编写JUnit测试时,我想知道以下之间是否有任何技术差异:

选项 1: 定义一个 setup 方法,即用 @Before 注释,在运行任何 @Test 方法之前初始化测试夹具状态。

选项 2: 定义一个私有方法 - 只是一个没有任何注释的普通旧私有方法 - 执行相同的初始化,并使每个 @Test 方法的第一行调用此方法。 (忽略有人忘记在每次测试中调用该方法的可能性。我正在寻找技术差异,而不是人为因素)

选项 2 示例:

public class MyTest {

    private void runSetupLogic() {
        // whatever @Before method would have done
    }

    @Test
    public void testMethod1() {
        runSetupLogic();
        // test logic
    }

    @Test
    public void testMethod2() {
        runSetupLogic();
        // test logic
    }
}

【问题讨论】:

  • 你为什么不看the doc
  • 主要区别在于@Before 是自动的,不依赖于测试方法作者。很难理解你为什么不利用它。
  • 谢谢,阿法辛。我想这就是您要指出的:“超类的 @Before 方法将在当前类的方法之前运行。”
  • @EJP:这个优势当然存在,但我想知道是否存在任何技术差异。我之所以问,是因为我刚刚阅读了一个关于使用 @BeforeClass 或在测试类构造函数中进行一次性初始化的区别的问题。有区别,答案解释了这一点。所以我想检查一下@Before 的特别之处

标签: java junit


【解决方案1】:

它们并不真正完全相同相同,但无论出于何种意图和目的,任何一种方式都应该没问题。但是,如果您对技术分析感兴趣,那么我对 Github 上当前 JUnit 4 代码的模糊理解如下:

当您使用默认的 JUnit 4 运行器 src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java 使用 @Before 时,实际运行的代码似乎是这样的:

/**
 * Returns a {@link Statement}: run all non-overridden {@code @Before}
 * methods on this class and superclasses before running {@code next}; if
 * any throws an Exception, stop execution and pass the exception on.
 */
protected Statement withBefores(FrameworkMethod method, Object target,
        Statement statement) {
    List<FrameworkMethod> befores = getTestClass().getAnnotatedMethods(
            Before.class);
    return befores.isEmpty() ? statement : new RunBefores(statement,
            befores, target);
}

以上在src/main/java/org/junit/internal/runners/statements/RunBefores.java中调用RunBefores:

public class RunBefores extends Statement {
    private final Statement next;

    private final Object target;

    private final List<FrameworkMethod> befores;

    public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) {
        this.next = next;
        this.befores = befores;
        this.target = target;
    }

    @Override
    public void evaluate() throws Throwable {
        for (FrameworkMethod before : befores) {
            before.invokeExplosively(target);
        }
        next.evaluate();
    }

invokeExplosively 方法定义在src/main/java/org/junit/runners/model/FrameworkMethod.java:

public Object invokeExplosively(final Object target, final Object... params)
        throws Throwable {
    return new ReflectiveCallable() {
        @Override
        protected Object runReflectiveCall() throws Throwable {
            return method.invoke(target, params);
        }
    }.run();
}

这似乎使用反射来调用带有@Before 注释的方法。

无论如何,希望这个答案是正确的,但我不确定。如果有人有任何更正,我可以从 cmets 中编辑它们。顺便说一下,这里参考的是@Before注解的javadoc:http://junit.org/javadoc/latest/org/junit/Before.html

【讨论】:

  • 谢谢,克里斯蒂安和斯特凡。我知道所有超类的@Before 方法都会被调用(就像构造函数一样),但是当我问这个问题时不知何故忘记了这一点。非常感谢。
【解决方案2】:

我不这么认为。
但是,如果您要实现另一个函数,例如 tearDown(),它本质上将用作 @After 方法,我认为您也可以这样做为了可读性,为了其他合作者甚至你自己的利益,使用它们。

使用 @Before@After 注释的好处是它们避免了在每个单元测试开始时调用方法,旨在为您节省额外的维护。如果由于某种原因您忘记将调用添加到您的 setUp() 和/或 tearDown() 方法,谁知道会出现什么问题。

当然,如果您在每次测试前都需要完全相同的设置,那么这是当然的。如果您设想为不同的单元测试设置完全不同的设置,那么也许您应该查看您正在测试的类的功能并问自己是否可以进行更多模块化。

【讨论】:

  • 我已经提到有人忘记拨打runSetupLogic() 并不是我想要的那种差异。 Christian Wilkie(由 Stefan Birkner 编辑)的答案正是我想要的。我真傻,居然忘记了。 :-)
【解决方案3】:

收益来自报告。

在你的方法中:runSetupLogic()

从测试开始运行时,会在测试中报告。不是设置的一部分,也不是初始化的一部分。

如果设置方法失败,您会得到关于失败原因的准确描述...设置与测试。

before 方法允许您将测试失败与设置失败隔离开来,并允许报告解决方案也知道。

【讨论】:

    猜你喜欢
    • 2017-08-29
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    相关资源
    最近更新 更多