Junit @ Before / @ After는 어떤 주문입니까?


133

통합 테스트 스위트가 있습니다. IntegrationTestBase모든 시험을 연장 할 수있는 수업이 있습니다. 이 기본 클래스에는 API 및 DB 연결을 설정 하는 @Before( public void setUp()) 및 @After( public void tearDown()) 메소드가 있습니다. 내가 해왔 단지 각 테스트 케이스에서 그 두 가지 방법을 무시하고 호출 super.setUp()하고 super.tearDown(). 그러나 누군가가 수퍼에게 전화하는 것을 잊거나 잘못된 장소에두면 예외가 발생하고 마지막으로 또는 뭔가를 슈퍼라고 부르는 것을 잊어 버린 경우 문제가 발생할 수 있습니다.

내가하고 싶은 것은 만들 것입니다 setUptearDown기본 클래스의 메소드를 final다음 단지 우리 자신의 주석 추가 @Before@After방법을. 초기 테스트를 수행하면 항상 다음 순서로 호출되는 것으로 보입니다.

Base @Before
Test @Before
Test
Test @After
Base @After

하지만 주문이 보장되지 않아 문제가 발생할 수 있다고 우려합니다. 나는 둘러 보았고 주제에 대해 아무것도 보지 못했습니다. 내가 할 수 있고 문제가 없는지 아는 사람이 있습니까?

암호:

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}


public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1
MyTest누락 extends?
aioobe

@aioobe : 더 이상은 아닙니다 :)
Joel

답변:


135

예,이 동작은 보장됩니다.

@Before:

@Before수퍼 클래스 의 메소드는 현재 클래스에서 대체되지 않는 한 현재 클래스 의 메소드보다 먼저 실행됩니다. 다른 순서는 정의되어 있지 않습니다.

@After:

@After수퍼 클래스에 선언 된 메소드는 현재 클래스에서 재정의되지 않는 한 현재 클래스 의 메소드 이후에 실행됩니다.


15
명확히하기 위해, 모든 @Before방법의 실행 순서 가 보장되는 것은 아니다. 10 개의 @Before방법이있는 경우 각 방법을 임의의 순서로 실행할 수 있습니다. 다른 방법 직전에.
Swati

5
다소 모호한 문서를 인용하는 대신 자신의 말로 설명해 주시겠습니까? 인가 @Before@After방법은 이전에 실행 모든 (클래스 당 한 번) 단지 클래스 메소드의 전체 제품군 전후에 다른 클래스 메소드 (방법에 한 번), 또는?
BT

5
John Q Citizen이 발표 한 중요한 내용을 참조하십시오. "@Before로 표시된 각 메소드가 클래스 계층 구조에서 고유 한 이름을 가진 경우에만 적용됩니다."
Bruno Bossola

클래스의 @Before (d) 메소드와 동일한 수퍼 클래스의 다른 메소드에서 동일한 메소드 이름을 사용하여 이름이 충돌했습니다 junit-4.12.
Stephane

이 규칙이 ConcordionRunner의 @BeforeExample 메소드에도 적용됩니까?
Adrian Pronk 2018 년

51

전에 나를 물린 한 가지 잠재력이 있습니다.

@Before클래스 @Before내에서 정의 된 메소드 를 실행하는 순서 가 보장되지 않기 때문에 각 테스트 클래스마다 최대 하나의 메소드 를 갖고 싶습니다 . 일반적으로 이러한 메소드를 호출합니다 setUpTest().

그러나 @Before로 문서화되어 있지만 The @Before methods of superclasses will be run before those of the current class. No other ordering is defined.이것은 @Before클래스 계층에서 고유 한 이름을 가진 각 메소드 가있는 경우에만 적용됩니다 .

예를 들어, 나는 다음을 가졌다 :

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

나는 AbstractFooTest.setUpTest()전에 달릴 것으로 예상 FooTest.setUpTest()했지만 FooTest.setupTest()처형되었다. AbstractFooTest.setUpTest()전혀 호출되지 않았습니다.

작동하려면 코드를 다음과 같이 수정해야합니다.

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

왜 기본 클래스에서 @Before 메소드의 이름을 바꾸지 않겠습니까? 이렇게하면 모든 어린이들에게 슈퍼를 불러야하는 것을 막을 수 있습니다. 어쨌든 같은 이름의 문제로 좋은 포수
Lawrence Tierney

24
이름을 충돌시키지 않으려면 이름 충돌을 피하기 위해 기본 클래스에서 @Before/ @After메소드를 만들 수 final있으므로 (실수로) 서브 클래스에서 재정의하려고하면 컴파일러가 불만을 제기합니다.
Stefan Winkler

4
같은 이름의 상위 메소드가 실행되지 않는 것은 JUnit 동작처럼 들리지 않습니다. OOP에서 기본적인 오버라이드가 어떻게 작동하는지 들립니다. 부모 메서드는 기본적으로 런타임에 존재하지 않습니다. 아이는 모든 의도와 목적으로 그것을 대체합니다. 이것이 Java의 작동 방식입니다.
Brandon

1
또 다른 문제는 부모 클래스가 공개되어야한다는 것입니다. 그렇지 않으면 @Before하위 클래스에도 메서드가있는 경우 표시된 메서드는 무시됩니다 @Before.
rusins

21

나는의 문서를 기반으로 생각 @Before하고 @After올바른 결론은 방법에게 고유 한 이름을 제공하는 것입니다. 테스트에서 다음 패턴을 사용합니다.

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

public class Test extends AbstractBaseTest {

  @Before
  public void setUp() {
    System.out.println("Test.setUp");
  }

  @After
  public void tearDown() {
    System.out.println("Test.tearDown");
  }

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

결과적으로 주다

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

이 방법의 장점 : AbstractBaseTest 클래스의 사용자는 실수로 setUp / tearDown 메소드를 대체 할 수 없습니다. 그들이 원한다면 정확한 이름을 알아야 할 수 있습니다.

이 접근 방식의 단점 : 사용자는 setUp / tearDown 이전 또는 이후에 발생하는 상황을 알 수 없습니다. 그들은 이러한 것들이 추상 클래스에 의해 제공된다는 것을 알아야합니다. 그러나 그것이 추상적 클래스를 사용하는 이유라고 생각합니다.


2
좋은 예-두 개의 @Test 메소드가있는 경우 더 설명이 좋으므로 setUp 및 tearDown이 테스트 메소드를 래핑하는 것을 볼 수 있습니다 .
Mark

이것이 OP에 대한 최상의 답변의 기초라고 생각하지만 독립형에 대한 답변을 작성해야합니다. 다른 사람들이 제안한 대안을 다루고 당신의 제안이 우월한 이유를 설명하기 위해 모범을 강화할 수 있습니까?
wachr

2

상황을 바꾸면 기본 클래스 추상을 선언하고 자손이 기본 클래스의 주석이 달린 setUp 및 tearDown 메서드에서 호출되는 setUp 및 tearDown 메서드 (주석없이)를 선언하도록 할 수 있습니다.


1
나쁘지 않은 생각이지만, 자체 setUp / tearDown이 필요없는 테스트에 대한 계약을 시행하고 싶지 않습니다
Joel

2

@BeforeClass주석을 사용 하여 setup()항상 먼저 호출 되도록 할 수 있습니다 . 마찬가지로 @AfterClass주석을 사용 하여 tearDown()항상 마지막에 호출 되도록 할 수 있습니다 .

이것은 일반적으로 권장되지 않지만 지원됩니다 .

정확히 원하는 것은 아니지만 테스트가 실행되는 동안 DB 연결을 기본적으로 열어 놓은 다음 끝까지 한 번에 닫으십시오.


2
이 작업을 수행하는 경우 실제로, 나는 방법을 만들 것을 권 해드립니다 setupDB()closeDB()와 그들을 표시 @BeforeClass@AfterClass와와 방법 후 전 / 교체 setup()tearDown()
스와티어

주석 방법 @BeforeClass@AfterClass필요 정적합니다. 이러한 메소드 내에서 인스턴스 변수를 사용하려는 경우는 어떻습니까?
Pratik Singhal

@BeforeClassPowermock과 함께 사용시 경고 : 첫 번째 테스트 실행에서만 작동합니다. 이 문제를 참조하십시오 : github.com/powermock/powermock/issues/398
Dagmar

2

이것은 태그 라인 질문에 대한 답변이 아니지만 질문 본문에 언급 된 문제에 대한 답변입니다. @Before 또는 @After를 사용하는 대신 @ org.junit.Rule 을 사용 하면 유연성이 향상됩니다. 연결을 관리하는 경우 ExternalResource (4.7 현재)가 가장 관심이있는 규칙입니다. 또한 규칙의 실행 순서를 보장하려면 RuleChain (4.10 기준)을 사용하십시오. 이 질문을 받았을 때이 모든 것이 가능하다고 생각합니다. 아래 코드 예제는 ExternalResource의 javadoc에서 복사되었습니다.

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.