上课前的Junit(非静态)


84

是否有任何最佳实践让Junit在测试文件中执行一次功能,并且它也不应该是静态的。

@BeforeClass非静态功能?

这是一个丑陋的解决方案:

@Before void init(){
    if (init.get() == false){
        init.set(true);
        // do once block
    }
}

好吧,这是我不想做的事情,我正在寻找集成的junit解决方案。


好吧,我的测试文件和基础测试文件的层次结构相当大,我需要有可能在子测试类中覆盖此操作。
罗马2010年

1
我遇到了同样的问题,其中只有许多参数化测试中的第一个应该执行登录。
dokaspar 2012年

5
请注意,“丑陋”的解决方案(与纯JUnit一起使用)不考虑撕裂测试。
eskatos 2013年

Answers:


22

如果您不想为一次初始化设置静态初始化程序,并且不打算使用JUnit,请看一下TestNG。TestNG通过各种配置选项支持非静态的一次性初始化,所有配置选项均使用注释。

在TestNG中,这等效于:

@org.testng.annotations.BeforeClass
public void setUpOnce() {
   // One time initialization.
}

拆解

@org.testng.annotations.AfterClass
public void tearDownOnce() {
   // One time tear down.
}

对于等同于JUnit 4@Before和的TestNG @After,可以分别使用@BeforeMethod@AfterMethod


41

一个简单的if语句似乎也可以很好地工作:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:test-context.xml"})
public class myTest {

    public static boolean dbInit = false;

    @Autowired
    DbUtils dbUtils;

    @Before
    public void setUp(){

        if(!dbInit){

            dbUtils.dropTables();
            dbUtils.createTables();
            dbInit = true;

        }
    }

 ...

1
漂亮又简单!但是,看不到一种方法可以简单地对其进行调整,以使其成为@AfterClass在所有测试运行后都将消失的非静态等效项吗?
史蒂夫·钱伯斯

1
有关此方法的更新,请参见此处,该方法适用于使用继承的测试类。
史蒂夫·钱伯斯

36

使用空构造函数是最简单的解决方案。您仍然可以在扩展类中重写构造函数。

但是对于所有继承来说,它并不是最佳的。这就是JUnit 4改用注释的原因。

另一种选择是在factory / util类中创建一个辅助方法,并让该方法完成工作。

如果您使用的是Spring,则应考虑使用@TestExecutionListeners注释。像这样的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({CustomTestExecutionListener.class, 
     DependencyInjectionTestExecutionListener.class})
@ContextConfiguration("test-config.xml")
public class DemoTest {

AbstractTestExecutionListener例如,Spring包含此空方法,您可以重写该方法:

public void beforeTestClass(TestContext testContext) throws Exception {
    /* no-op */
}

注意:DependencyInjectionTestExecutionListener添加custom时不要忽略/遗漏TestExecutionListeners。如果这样做,所有的自动接线都将是null


+1该技术解决了我想使用DbUnit且每个类仅加载数据集一次的问题
Brad

+1这是完美的...对于不依赖于Spring的古代版本的人们。:(
Mike Miller

1
是否beforeTestClass()在上下文初始化之前或之后调用此方法?
2015年

@Dims上下文初始化
阿南德Rockzz


0

我从未尝试过,但是也许您可以创建一个无参数的构造函数并从那里调用函数?


这将起作用,问题是我需要在扩展此基础测试类的类中覆盖此操作
罗马,2010年

@罗马:哦,现在我明白了。将此添加到您的帖子,此评论使事情变得更加清晰。
罗马2010年

测试用例存在的次数将多次调用构造函数。对于每个测试方法,将创建新的Test类对象。因此,在这里使用构造函数不是解决方案
manikanta

同样,这将不能依赖于已经构造的对象的依赖注入。
Mike Miller

0

本文讨论了此问题的两个非常好的解决方案:

  1. 使用自定义Runner的“干净” junit(使用界面,但是您可以使用自定义注释扩展它,例如@BeforeInstance)
  2. 正如Espen之前提到的,Spring执行监听器。

0

更新:请参阅Cherry的评论,以了解以下建议存在缺陷的原因。(将答案保留在此处而不是删除,因为注释可能会为其他人提供有用的信息,说明为什么此方法不起作用。)


如果使用依赖项注入(例如Spring),则值得考虑的另一种选择是@PostConstruct。这将确保依赖项注入已完成,而在构造函数中则不会这样:

@PostConstruct
public void init() {
    // One-time initialization...
}


7
如果进行Junit测试,则是非常糟糕的解决方案。Junit每次运行测试方法时都会创建测试类实例。因此,如果类中有6个测试方法,则一个类构造函数@Before@After方法将被调用6次!因此,在这种情况下,@PostConstruct其行为类似于@Before注释。您可以简单地对其进行测试:只需将2个测试方法放在测试类中,添加@PostConstruct public void init() {System.out.println("started");}并在日志中查看打印了多少次。
2014年

有关信息,我刚看过JUnit文档,该文档确认了上面注释中有关JUnit为每次@Test运行创建实例的描述:“要运行该方法,JUnit首先构造该类的新实例,然后调用带注释的方法。”
史蒂夫·钱伯斯

-2

只需使用@BeforeClass

@BeforeClass
public static void init() {
}

init非静态是没有意义的,因为每个测试都在单独的实例中运行。该实例init的运行将不匹配任何测试的实例。

您可能希望它为非静态的唯一原因是在子类中重写它,但是您也可以使用静态方法来做到这一点。只要使用相同的名称,就只会init调用子类方法。


2
整个问题是关于以非静态方式执行此操作的可能性,如果您在类上需要一些实例变量,则需要这样做。
西蒙·佛斯伯格

@SimonForsberg是的,我是说问题是XY问题。行动人士说,问题在于压倒了子班的行为。如果该示例需要实例变量,那么我可能会提出其他建议。
fgb


@SimonForsberg这就是我在说的评论。那呢
fgb
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.