JUnit 5에서는 모든 테스트 전에 코드를 실행하는 방법
@BeforeAll
주석은 클래스의 모든 테스트 전에 실행할 메서드를 표시합니다.
http://junit.org/junit5/docs/current/user-guide/ #기입설명서
하지만 모든 수업에서 모든 테스트 전에 코드를 실행할 수 있는 방법이 있을까요?
테스트에서 특정 데이터베이스 접속 세트를 사용하는지 확인하고 테스트를 실행하기 전에 이들 접속의 글로벌한 일회성 설정을 수행해야 합니다.
JUnit5에서는 커스텀 Extension을 생성하여 셧다운 후크를 루트테스트 컨텍스트에 등록할 수 있게 되었습니다.
내선번호는 다음과 같습니다.
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
}
}
그런 다음 이 작업을 한 번 이상 실행해야 하는 모든 테스트 클래스에 주석을 달 수 있습니다.
@ExtendWith({YourExtension.class})
여러 클래스에서 이 확장을 사용하는 경우 시작 및 종료 로직은 한 번만 호출됩니다.
@Phillip Gayret에서 이미 제공된 답변에 병렬로 JUnit을 테스트할 때 문제가 발생했습니다(즉,junit.jupiter.execution.parallel.enabled = true
를 참조해 주세요.
따라서 솔루션을 다음과 같이 수정했습니다.
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
}
}
아래 JUnit5는 자동 내선번호 등록을 제공합니다.그러기 위해서는 를 추가합니다.src/test/resources/
라는 전화번호부/META-INF/services
.org.junit.jupiter.api.extension.Extension
파일에 를 들어 이 파일에 클래스의 완전한 기밀 이름을 추가합니다.
at.myPackage.BeforeAllTestsExtension
동일한 Junit 구성 파일에서 다음 활성화
junit.jupiter.extensions.autodetection.enabled=true
이것에 의해, 확장이 모든 테스트에 자동적으로 첨부됩니다.
@Phillip의 제안에 따라 보다 완전한 코드 스니펫을 소개합니다.
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 { ... }
}
테스트 실행 순서는 다음과 같습니다.
DemoSetupExtension.setup (*)
TestOne.beforeAllTestOne
TestOne.testOne
TestOne.afterAllTestOne
TestTwo.beforeAllTestTwo
TestTwo.testTwo
TestTwo.afterAllTestTwo
DemoSetupExtension.close (*)
...이것은 단일 @Test(예: TestOne.testOne) 또는 전체 테스트 클래스(TestOne) 또는 다중/모든 테스트를 실행하도록 선택한 경우에도 마찬가지입니다.
각는, 「」를 할 수 .static
BeforeAll
(미국의) §:
interface UsesDatabase {
@BeforeAll
static void initializeDatabaseConnections() {
// initialize database connections
}
}
이 메서드는 구현 클래스별로1회 호출되므로 접속을 1회만 초기화하고 다른 콜에 대해서는 아무것도 하지 않는 방법을 정의해야 합니다.
나는 그것을 하는 방법을 알지 못한다.
@BeforeAll의 모든 코드가 특정 싱글톤을 호출하여 init이 동작하도록 하는 것(아마도 반복을 피하기 위한 게으른 방법으로)을 확인하는 것 뿐입니다.
아마 편리하지 않을 거야...기타 옵션으로는 특정 JVM 작업 내에서 테스트가 실행된다고 가정합니다.에이전트를 JVM 실행에 후크할 수 있습니다.그렇게 하면,
그 이상: 두 제안 모두 왠지 내게는 해킹처럼 들린다.제 눈에는 진정한 답이 있습니다. 한 발짝 물러서서 환경에 의존하는지 꼼꼼히 살펴보세요.그런 다음 테스트가 시작되고 "올바른 일"이 자동으로 수행되도록 환경을 준비할 방법을 찾습니다.즉, 이 문제의 원인이 된 아키텍처를 조사하는 것을 검토해 주십시오.
위의 조언은 나에게 효과가 없기 때문에 나는 이 문제를 다음과 같이 해결했다.
기본 추상 클래스(즉, setUpDriver() 메서드에서 드라이버를 초기화하는 추상 클래스)에 코드의 다음 부분을 추가합니다.
private static boolean started = false;
static{
if (!started) {
started = true;
try {
setUpDriver(); //method where you initialize your driver
} catch (MalformedURLException e) {
}
}
}
테스트 클래스가 Base Abstract class -> setUpDriver() 메서드에서 확장되는 경우 첫 번째 @Test는 프로젝트 실행당 한 번만 실행됩니다.
@Mihnea Giurgea의 뒤를 이어 @Phillip Gayret의 매우 좋은 답변에 대한 나의 POC 개선점을 소개합니다.
서브 질문:공유 싱글톤리소스에 접속하려면 어떻게 해야 하나요? (아마도 궁금하실 거예요...)
사이드바:실험을 하는 동안, 여러 개를 사용하는 것이@ExtendWith(...)
제대로 둥지를 튼 것 같아요.그렇다고 해도, 어느 시점에서는 그렇게 동작하지 않았던 것이 기억되기 때문에, 사용 케이스가 올바르게 동작하고 있는 것을 확인해 주세요.
아마 바쁘기 때문에 디저트가 먼저입니다.다음은 "폴더 내의 모든 테스트"를 실행한 결과입니다.
NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Colors::blue - resource=Something nice to share!
Colors::gold - resource=Something nice to share!
Base::afterAll
Base::beforeAll
Numbers::one - resource=Something nice to share!
Numbers::tre - resource=Something nice to share!
Numbers::two - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)
물론 단일 테스트 클래스를 실행하는 것만으로 얻을 수 있는 이점은 다음과 같습니다.
NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Numbers::one - resource=Something nice to share!
Numbers::tre - resource=Something nice to share!
Numbers::two - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)
그리고 이제 예상할 수 있는 대로 특정 테스트:
NestedSingleton::beforeAll (setting resource)
Singleton::Start-Once
Base::beforeAll
Colors::gold - resource=Something nice to share!
Base::afterAll
Singleton::Finish-Once
NestedSingleton::close (clearing resource)
내 말 듣고 있어?그럼 실제 코드를 보는 걸 즐기실 수 있을 거야
======================================================
junitsingletonresource/Base.java
======================================================
package junitsingletonresource;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith({Singleton.class})
public abstract class Base extends BaseNestedSingleton
{
@BeforeAll public static void beforeAll() { System.out.println("Base::beforeAll"); }
@AfterAll public static void afterAll () { System.out.println("Base::afterAll" ); }
}
======================================================
junitsingletonresource/Colors.java
======================================================
package junitsingletonresource;
import org.junit.jupiter.api.Test;
public class Colors extends Base
{
@Test public void blue() { System.out.println("Colors::blue - resource=" + getResource()); }
@Test public void gold() { System.out.println("Colors::gold - resource=" + getResource()); }
}
======================================================
junitsingletonresource/Numbers.java
======================================================
package junitsingletonresource;
import org.junit.jupiter.api.Test;
public class Numbers extends Base
{
@Test public void one() { System.out.println("Numbers::one - resource=" + getResource()); }
@Test public void two() { System.out.println("Numbers::two - resource=" + getResource()); }
@Test public void tre() { System.out.println("Numbers::tre - resource=" + getResource()); }
}
======================================================
junitsingletonresource/BaseNestedSingleton.java
======================================================
package junitsingletonresource;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
/**
* My riff on Phillip Gayret's solution from: https://stackoverflow.com/a/51556718/5957643
*/
@ExtendWith({BaseNestedSingleton.NestedSingleton.class})
public abstract class BaseNestedSingleton
{
protected String getResource() { return NestedSingleton.getResource(); }
static /*pkg*/ class NestedSingleton implements BeforeAllCallback, ExtensionContext.Store.CloseableResource
{
private static boolean initialized = false;
private static String resource = "Tests should never see this value (e.g. could be null)";
private static String getResource() { return resource; }
@Override
public void beforeAll(ExtensionContext context)
{
if (!initialized) {
initialized = true;
// The following line registers a callback hook when the root test context is shut down
context.getRoot().getStore(GLOBAL).put(this.getClass().getCanonicalName(), this);
// Your "before all tests" startup logic goes here, e.g. making connections,
// loading in-memory DB, waiting for external resources to "warm up", etc.
System.out.println("NestedSingleton::beforeAll (setting resource)");
resource = "Something nice to share!";
}
}
@Override
public void close() {
if (!initialized) { throw new RuntimeException("Oops - this should never happen"); }
// Cleanup the resource if needed, e.g. flush files, gracefully end connections, bury any corpses, etc.
System.out.println("NestedSingleton::close (clearing resource)");
resource = null;
}
}
}
======================================================
junitsingletonresource/Singleton.java
======================================================
package junitsingletonresource;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
/**
* This is pretty much what Phillip Gayret provided, but with some printing for traceability
*/
public class Singleton implements BeforeAllCallback, ExtensionContext.Store.CloseableResource
{
private static boolean started = false;
@Override
public void beforeAll(ExtensionContext context)
{
if (!started) {
started = true;
System.out.println("Singleton::Start-Once");
context.getRoot().getStore(GLOBAL).put("any unique name", this);
}
}
@Override
public void close() { System.out.println("Singleton::Finish-Once"); }
}
언급URL : https://stackoverflow.com/questions/43282798/in-junit-5-how-to-run-code-before-all-tests
'programing' 카테고리의 다른 글
jinja python 템플릿에서 쉼표로 구분된 목록을 출력하려면 어떻게 해야 합니까? (0) | 2022.10.31 |
---|---|
iframe의 폭과 높이를 콘텐츠에 맞게 조정 (0) | 2022.10.31 |
Comparator를 사용하여 커스텀 정렬 순서를 정의하려면 어떻게 해야 합니까? (0) | 2022.10.31 |
SQL에서 Lef-most 조건을 충족하는 첫 번째 레코드를 가져오는 중 (0) | 2022.10.31 |
URL Connection이란 정확히 어떤 것입니까?setDoOutput()이 영향을 미칩니까? (0) | 2022.10.22 |