【问题标题】:Junit - run set up method onceJunit - 运行一次设置方法
【发布时间】:2012-08-18 18:03:19
【问题描述】:

我设置了一个包含几个测试的类,而不是使用@Before,我希望有一个在所有测试之前只执行一次的设置方法。 Junit 4.8 可以吗?

【问题讨论】:

标签: java junit


【解决方案1】:

虽然我同意@assylias 的观点,即使用@BeforeClass 是一个经典的解决方案,但并不总是很方便。带有@BeforeClass 注释的方法必须是静态的。对于一些需要测试用例实例的测试来说非常不方便。例如,基于 Spring 的测试使用 @Autowired 来处理在 spring 上下文中定义的服务。

在这种情况下,我个人使用带有 @Before 注释的常规 setUp() 方法并管理我的自定义 static(!) boolean 标志:

private static boolean setUpIsDone = false;
.....
@Before
public void setUp() {
    if (setUpIsDone) {
        return;
    }
    // do the setup
    setUpIsDone = true;
}

【讨论】:

  • 在 Kenny Cason 的评论中添加了为什么它必须是静态的。它必须是静态的,因为 JUnit 会为每个 @Test 方法实例化一个新的测试类实例。如果实例变量不是静态的,则每个实例的实例变量都将重置为其默认值 (false)。查看更多信息:martinfowler.com/bliki/JunitNewInstance.html
  • 除了setUp() 方法位于超类中的情况外,此方法有效 - 在下方发布了answer 尝试解决此问题。
  • 我不敢对有 84k 代表的人说这个,但 BeforeClass 实际上并没有回答这个问题:BeforeClass 是在每个测试课程开始时运行的。但是 OP 要求一个“在所有测试之前只运行一次”的。您提出的解决方案可以做到这一点,但您必须让所有测试类扩展“CommonTest”类...
  • @mikerodent,恕我直言,OP 询问了他的测试用例中的所有测试,而不是所有测试。因此,您的评论不太相关。顺便说一句,即使他的声誉很高,也不要担心对任何人说什么。至少这是我所做的:)。 2012 年 8 月,当我回答这个问题时,我的声誉明显下降。
  • 不适用于我的情况,设置中初始化的变量在每次测试后都会重置,所以只初始化一次是没有意义的。
【解决方案2】:

你可以使用the BeforeClass annotation:

@BeforeClass
public static void setUpClass() {
    //executed only once, before the first test
}

【讨论】:

  • 我不能使用这个,我有几个基于非静态组件的设置方法,例如 getClass()
  • @Bober02 BeforeClass 确实需要是静态的。如果您不能使用它,另一个答案提供了一种解决方法。
  • 确定不能使用TheClassYouWant.class 代替 getClass() 调用吗?这是实际的 Java:String.class.getName()
  • @mikerodent 我把这个问题理解为“课堂上的所有测试”——但你是对的,它可能不是 OP 想要的。
【解决方案3】:

JUnit 5 现在有一个 @BeforeAll 注释:

表示注解的方法应该在所有@Test之前执行 当前类或类层次结构中的方法;类似于 JUnit 4 的@BeforeClass。此类方法必须是静态的。

JUnit 5 的生命周期注释似乎终于搞定了!您甚至无需查看即可猜出哪些注释可用(例如@BeforeEach @AfterAll)

【讨论】:

  • @BeforeClass有同样的问题,需要static。 IMO @AlexR 的解决方案更好。
  • @zengr 倾向于同意你的观点:正如我对 AlexR 所说,如果它只运行一次,他的解决方案要求所有测试类从 CommonTest 类继承。但这很简单,恕我直言,当该语言提供简单的机制时,您可能不应该使用“花哨的”框架提供的解决方案。当然,除非有充分的理由。此外,使用像他这样简单的东西,带有一个好的“做它在锡上所说的”类型名称,有助于提高可读性。
  • 话虽如此,恕我直言,似乎有更多理由使用“AfterAll”注释:设计一种检测所有测试何时完成的机制是非常困难和人为的。相反,当然,纯粹主义者可能会说您永远不必进行“最终清理”,即每次“拆卸”都应该使所有资源处于原始状态……他们可能是对的!
  • 这是否适用于有多个模块的 Maven,每个模块都有自己的测试?
  • @mike rodent,就我而言,在每次测试之前/之后在文件系统中设置和拆除测试文件似乎会导致文件死锁。目前,我已经独立地了解了 AlexR 设置一次的解决方案。我有两个静态标志,已经设置和脏。如果最初检测到脏状态,或者设置失败导致脏状态,则 setup() 调用 cleanup()。为了在运行测试后进行清理,我再次运行它们。凌乱,根本不理想,不在我们的构建过程中。仍在寻找更好的方法(jUnit 4.12)。
【解决方案4】:

setUp()在测试类的超类中时(例如下面的AbstractTestBase),接受的答案可以修改如下:

public abstract class AbstractTestBase {
    private static Class<? extends AbstractTestBase> testClass;
    .....
    public void setUp() {
        if (this.getClass().equals(testClass)) {
            return;
        }

        // do the setup - once per concrete test class
        .....
        testClass = this.getClass();
    }
}

这应该适用于单个非静态 setUp() 方法,但我无法为 tearDown() 生成等效方法,而不会误入复杂反射的世界......赏金指向任何可以的人!

【讨论】:

    【解决方案5】:

    编辑: 我刚刚在调试时发现该类也在每次测试之前实例化。 我猜@BeforeClass 注释在这里是最好的。

    你也可以在构造函数上设置,测试类毕竟是一个类。 我不确定这是否是一种不好的做法,因为几乎所有其他方法都已注释,但它确实有效。你可以像这样创建一个构造函数:

    public UT () {
        // initialize once here
    }
    @Test
    // Some test here...
    

    ctor 将在测试之前被调用,因为它们不是静态的。

    【讨论】:

      【解决方案6】:

      使用 Spring 的 @PostConstruct 方法完成所有初始化工作,并且此方法在任何 @Test 执行之前运行

      【讨论】:

        【解决方案7】:

        JUnit 5 @BeforeAll 可以是非静态的,前提是测试类的生命周期是每个类的,即用 @TestInstance(Lifecycle.PER_CLASS) 注释测试类,然后你就可以开始了

        【讨论】:

          【解决方案8】:

          试试这个解决方案: https://stackoverflow.com/a/46274919/907576

          使用 @BeforeAllMethods/@AfterAllMethods 注释,您可以在实例上下文中执行 Test 类中的任何方法,其中所有注入的值都可用。

          【讨论】:

          • 依赖第三方库。
          【解决方案9】:

          我的肮脏解决方案是:

          public class TestCaseExtended extends TestCase {
          
              private boolean isInitialized = false;
              private int serId;
          
              @Override
              public void setUp() throws Exception {
                  super.setUp();
                  if(!isInitialized) {
                      loadSaveNewSerId();
                      emptyTestResultsDirectory();
                      isInitialized = true;
                  }
              }
          
             ...
          
          }
          

          我将它用作我所有测试用例的基础。

          【讨论】:

          • 公共类 TestCaseExtended 扩展 TestCase { private static boolean isInitialized = false;私有静态TestCaseExtended caseExtended;私人 int serId; @Override public void setUp() 抛出异常 { super.setUp(); if (!isInitialized) { caseExtended = new TestCaseExtended(); caseExtended.loadSaveNewSerId(); caseExtended.emptyTestResultsDirectory(); isInitialized = true; } }
          【解决方案10】:

          如果您不想强制声明在每个子测试上设置和检查的变量,那么将其添加到 SuperTest 可以这样做:

          public abstract class SuperTest {
          
              private static final ConcurrentHashMap<Class, Boolean> INITIALIZED = new ConcurrentHashMap<>();
              protected final boolean initialized() {
                  final boolean[] absent = {false};
                  INITIALIZED.computeIfAbsent(this.getClass(), (klass)-> {
                      return absent[0] = true;
                  });
                  return !absent[0];
              }
          }
          
          
          
          public class SubTest extends SuperTest {
              @Before
              public void before() {
                  if ( super.initialized() ) return;
          
                   ... magic ... 
              }
          
          }
          

          【讨论】:

            【解决方案11】:

            我这样解决了这个问题:

            将这部分代码添加到您的 Base 抽象类(我的意思是您在 setUpDriver() 方法中初始化驱动程序的抽象类):

            private static boolean started = false;
            static{
                if (!started) {
                    started = true;
                    try {
                        setUpDriver();  //method where you initialize your driver
                    } catch (MalformedURLException e) {
                    }
                }
            }
            

            现在,如果您的测试类将从 Base 抽象类扩展 -> setUpDriver() 方法将在第一个 @Test 之前执行 ONE 每次运行的时间。

            【讨论】:

              【解决方案12】:

              这里有一个替代建议:

              我要做的是让这个工作 创建一个名为 _warmup 或只是 _ 的方法 用@FixMethodOrder(MethodSorters.NAME_ASCENDING)注释测试类

              这仅适用于您运行类中的所有测试

              它的缺点是包含额外的测试,这也会运行额外的@Before 和@After 通常建议您的测试方法与顺序无关,这违反了该规则,但是为什么有人希望在报告中随机排序测试我不知道所以 NAME_ASCENDING 是我一直使用的

              但这样做的好处是设置简单,代码最少,无需扩展类/运行器等... 测试运行长度更准确,因为所有设置时间都在方法 _warmup 上报告

              【讨论】:

                【解决方案13】:

                经过一段时间的试验,这是我的解决方案。我需要这个进行春季启动测试。我尝试使用@PostConstruct,不幸的是它为每个测试执行。

                public class TestClass {
                    private static TestClass testClass = null;
                    @Before
                    public void setUp() {
                        if (testClass == null) {
                            // set up once
                            ...
                            testClass = this;
                        }
                    }
                    @AfterClass
                    public static void cleanUpAfterAll() {
                        testClass.cleanUpAfterAllTests();
                    }
                    private void cleanUpAfterAllTests() {
                        // final cleanup after all tests
                        ...
                    }
                    @Test
                    public void test1() {
                        // test 1
                        ...
                    }
                    @Test
                    public void test2() {
                        // test 2
                        ...
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-10-13
                  相关资源
                  最近更新 更多