import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
public class YourExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
private static boolean started = false;
@Override
public void beforeAll(ExtensionContext context) {
if (!started) {
started = true;
// Your "before all tests" startup logic goes here
// The following line registers a callback hook when the root test context is shut down
context.getRoot().getStore(GLOBAL).put("any unique name", this);
}
}
@Override
public void close() {
// Your "after all tests" logic goes here
}
}
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public class BeforeAllTestsExtension extends BasicTestClass
implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
/**Gate keeper to prevent multiple Threads within the same routine */
private static final Lock LOCK = new ReentrantLock();
/**volatile boolean to tell other threads, when unblocked, whether they should try attempt start-up. Alternatively, could use AtomicBoolean. */
private static volatile boolean started = false;
@Override
public void beforeAll(final ExtensionContext context) throws Exception {
// lock the access so only one Thread has access to it
LOCK.lock();
try {
if (!started) {
started = true;
// Your "before all tests" startup logic goes here
// The following line registers a callback hook when the root test context is
// shut down
context.getRoot().getStore(GLOBAL).put("any unique name", this);
// do your work - which might take some time -
// or just uses more time than the simple check of a boolean
}
} finally {
// free the access
LOCK.unlock();
}
}
@Override
public void close() {
// Your "after all tests" logic goes here
}
}
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
public abstract class BaseSetupExtension
implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
// We need to use a unique key here, across all usages of this particular extension.
String uniqueKey = this.getClass().getName();
Object value = context.getRoot().getStore(GLOBAL).get(uniqueKey);
if (value == null) {
// First test container invocation.
context.getRoot().getStore(GLOBAL).put(uniqueKey, this);
setup();
}
}
// Callback that is invoked <em>exactly once</em>
// before the start of <em>all</em> test containers.
abstract void setup();
// Callback that is invoked <em>exactly once</em>
// after the end of <em>all</em> test containers.
// Inherited from {@code CloseableResource}
public abstract void close() throws Throwable;
}
使用方法:
public class DemoSetupExtension extends BaseSetupExtension {
@Override
void setup() {}
@Override
public void close() throws Throwable {}
}
@ExtendWith(DemoSetupExtension.class)
public class TestOne {
@BeforeAll
public void beforeAllTestOne { ... }
@Test
public void testOne { ... }
}
@ExtendWith(DemoSetupExtension.class)
public class TestTwo {
@BeforeAll
public void beforeAllTestTwo { ... }
@Test
public void testTwo { ... }
}
private static boolean started = false;
static{
if (!started) {
started = true;
try {
setUpDriver(); //method where you initialize your driver
} catch (MalformedURLException e) {
}
}
}
现在,如果您的测试类将extendsfrom Base abstract class -〉setUpDriver() 方法将在first @Test only之前执行ONE次,每次项目运行。
7条答案
按热度按时间s71maibg1#
在JUnit 5中,通过创建一个自定义的扩展,现在可以实现这一点,您可以从该扩展中在根测试上下文上注册一个shutdown挂接。
您的扩展将如下所示;
然后,您需要至少执行一次的任何测试类都可以使用以下内容进行注解:
在多个类上使用此扩展时,启动和关闭逻辑将只被调用一次。
svgewumm2#
@Philipp Gayret已经提供的答案在并行测试JUnit(即
junit.jupiter.execution.parallel.enabled = true
)时存在一些问题。因此,我将解决方案修改为:
如下所述,JUnit 5提供了一个自动扩展注册。为此,在
src/test/resources/
中添加一个名为/META-INF/services
的目录,并添加一个名为org.junit.jupiter.api.extension.Extension
的文件。将类的完全分类名称添加到该文件中,例如:下一个在同一Junit配置文件中启用
这样,扩展会自动附加到您的所有测试。
a1o7rhls3#
下面是一个更完整的代码片段,它是对@Philipp的建议的扩展:
使用方法:
测试执行顺序为:
...无论您选择运行单个@测试(例如TestOne.testOne)、整个测试类(TestOne)还是多个/所有测试,这都将是真的。
yquaqz184#
您可以使用定义
static
BeforeAll
的接口来标记使用数据库的每个测试类(这样它就不能被覆盖)。例如:对于每个实现类,此方法将被调用一次,因此您需要定义一种方法,以便仅初始化连接一次,然后不对其他调用执行任何操作。
gwo2fgha5#
我不知道有什么办法能做到这一点。
我只需要确保@BeforeAll的所有代码都调用某个单例来使init工作(可能是以一种懒惰的方式来避免重复)。
可能不太方便......我看到的唯一其他选择是:我假设您的测试在一个特定的JVM作业中运行。您 * 可以 * 将一个 * agent * 挂接到JVM运行中,这样就可以为您执行init工作。
除此之外:这两个建议听起来都像是在耍花招。在我看来,真实的的答案是:退一步,仔细检查环境的依赖关系。2然后找到一种方法来准备您的环境,使您的测试能够自动进行,并且“正确的事情”能够自动发生。3换句话说:请考虑调查一下导致此问题的体系结构。
1zmg4dgp6#
上面建议做而不是对我起作用,所以我这样解决了这个问题:
将以下代码添加到您的Base抽象类(我指的是您在 setUpDriver() 方法中初始化驱动程序的抽象类):
现在,如果您的测试类将extendsfrom Base abstract class -〉setUpDriver() 方法将在first @Test only之前执行ONE次,每次项目运行。
pgccezyw7#
以下是我对@菲利普Gayret给出的非常好的答案的POC改进,它追随了@Mihnea Giurgea的脚步。
我的子问题:***如何访问该共享单例资源?***(也许您也想知道...)
***边栏:在我的实验中,我发现使用多个
@ExtendWith(...)
似乎可以正确地嵌套。即使如此,我记得在我摸索的过程中,它在某些时候不是这样工作的,所以您应该***确保您的用例正确地工作[原文如此]。因为您可能很赶时间,* 先吃甜点:* 下面是运行“文件夹中的所有测试”的输出:
当然,只要运行一个测试类就可以:
而一个具体的考验,正如现在可以预料的那样:
还在听我说吗?那你可能会喜欢看实际的代码...