Junit-一次运行设置方法


Answers:


205

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

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

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

10
在肯尼·卡森(Kenny Cason)的评论中,它为什么必须是静态的。它必须是静态的,因为JUnit为每个@Test方法实例化测试类的新实例。如果实例变量不是静态的,则将其重置为每个实例的默认值(false)。看到更多信息:martinfowler.com/bliki/JunitNewInstance.html
dustin.schultz 2014年

2
setUp()方法在超类中的情况外,此方法有效-在下面尝试解决此问题的答案已发布。
史蒂夫·钱伯斯

4
我很犹豫地对84k代表说这句话,但是BeforeClass实际上并没有回答这个问题:BeforeClass在每个测试类的开头运行。但是,OP要求它运行“在所有测试之前仅运行一次”。您提出的解决方案可以做到这一点,但是您必须使所有测试类都扩展一个“ CommonTest”类……
mike rodent

1
@ mikerodent,IMHO OP询问了他的测试用例中的所有测试,而不是整个测试。因此,您的评论不太重要。顺便说一句,即使他的声誉很高,也不要担心对任何人说什么。至少这是我所做的:)。当我回答问题时,我的声誉在2012年8月大大降低了。
AlexR

在我的情况下不起作用,在每次测试后都会重置设置中初始化的变量,因此只初始化一次是没有意义的。
Aphax

89

您可以使用BeforeClass注释

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

12
我不能使用它,我有一些基于非静态组件(例如getClass())的设置方法
Bober02

1
@ Bober02 BeforeClass实际上必须是静态的。如果您不能使用它,另一个答案提供了一种解决方法。
assylias 2012年

2
确定不能使用TheClassYouWant.class而不是getClass()调用吗?这是实际的Java :String.class.getName()
stolsvik


1
@mikerodent我将问题理解为“类中的所有测试”,但是您说对了,这可能不是OP想要的。
assylias

29

JUnit 5现在具有@BeforeAll批注:

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

JUnit 5的生命周期注释似乎终于正确了!您甚至不用看就能猜出可用的注释(例如@BeforeEach @AfterAll)


6
它有同样的问题@BeforeClass,那就是必须static。IMO @AlexR的解决方案更好。
zengr

@zengr倾向于同意您的意见:正如我对AlexR所说的那样,如果要运行一次,他的解决方案要求所有测试类都从CommonTest类中继承。但这很简单,而且恕我直言,当语言提供一种简单的机制时,您可能不应该使用“花式”框架提供的解决方案。当然,除非有充分的理由。另外,使用像他这样的简单名称,并带有良好的“按锡说的话”类型名称,有助于提高可读性。
麦克啮齿动物

话又说回来,恕我直言,似乎有更多的理由使用“ AfterAll”注释:设计一种检测所有测试何时完成的机制将非常困难且人为。相反,当然,纯粹主义者可能会说您永远不必进行“最终清理”,即,每个“ tearDown”都应使所有资源处于原始状态……而他们可能是对的!
麦克啮齿动物

这适用于Maven,因为Maven有多个模块,每个模块都有测试?
Mark Boon

@mike rodent,就我而言,在每次测试之前/之后在文件系统中设置和删除测试文件似乎会导致文件死锁。现在,我已经独立提出了AlexR的设置一次解决方案。我有两个静态标志,已经设置和脏了。如果最初检测到脏状态,或者安装失败导致脏状态,则setup()调用cleanup()。为了在运行测试后进行清理,我再次运行它们。凌乱,一点都不理想,不在我们的构建过程中。仍在寻找更好的方法(jUnit 4.12)。
Rebeccah

9

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()不深入复杂的思考世界,我将无法产生等效的结果……赏金指向任何可以的人!


3

编辑: 我只是在调试的时候才发现,该类也要在每次测试之前实例化。我猜@BeforeClass批注是最好的。

您也可以在构造函数上进行设置,毕竟测试类一个类。我不确定这是否是一个不好的做法,因为几乎所有其他方法都带有注释,但是它可以工作。您可以创建一个类似的构造函数:

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

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



0

我肮脏的解决方案是:

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; private int serId; @Override public void setUp()引发异常{super.setUp(); 如果(!isInitialized){caseExtended = new TestCaseExtended(); caseExtended.loadSaveNewSerId(); caseExtended.emptyTestResultsDirectory(); isInitialized = true; }
欧比(Obi)

0

如果您不想强制声明在每个子测试中设置并检查的变量,则可以将其添加到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 ... 
    }

}

0

我这样解决了这个问题:

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

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

现在,如果你的测试类将从扩展基础抽象类- > setUpDriver()方法将被执行之前,首先@Test只ONE每次运行时间。


0

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

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.