Mam pakiet testów integracji. Mam IntegrationTestBase
klasę na wszystkie moje testy do rozszerzenia. Ta klasa bazowa ma metody @Before
( public void setUp()
) i @After
( public void tearDown()
) do nawiązywania połączeń API i DB. To, co robiłem, to po prostu zastąpienie tych dwóch metod w każdym przypadku testowym i wywołanie super.setUp()
i super.tearDown()
. Może to jednak powodować problemy, jeśli ktoś zapomni zadzwonić do super lub umieści go w niewłaściwym miejscu i zostanie wyrzucony wyjątek i zapomni w końcu zadzwonić do super.
Chcę tylko utworzyć metody setUp
i tearDown
w klasie bazowej, final
a następnie po prostu dodać własne metody @Before
i adnotacje @After
. Podczas wykonywania wstępnych testów wydaje się, że zawsze wywołuje w następującej kolejności:
Base @Before
Test @Before
Test
Test @After
Base @After
ale trochę się martwię, że zamówienie nie jest gwarantowane i może powodować problemy. Rozejrzałem się i nie widziałem nic na ten temat. Czy ktoś wie, czy mogę to zrobić i nie mam żadnych problemów?
Kod:
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?* }
}
źródło
MyTest
brakujeextends
?Odpowiedzi:
Tak, to zachowanie jest gwarantowane:
@Before
:@After
:źródło
@Before
metod nie jest gwarantowana. Jeśli istnieje 10@Before
metod, każdą z nich można wykonać w dowolnej kolejności; tuż przed jakąkolwiek inną metodą.@Before
i@After
są uruchamiane przed każdą inną metodą klasową (raz na metodę), czy tuż przed i po całym zestawie metod klasowych (raz na klasę)?junit-4.12
.Jeden potencjalny chwyt, który mnie wcześniej ugryzł:
Lubię mieć co najwyżej jedną
@Before
metodę w każdej klasie testowej, ponieważ kolejność uruchamiania@Before
metod zdefiniowanych w klasie nie jest gwarantowana. Zwykle nazwę taką metodęsetUpTest()
.Ale chociaż
@Before
jest to udokumentowane jakoThe @Before methods of superclasses will be run before those of the current class. No other ordering is defined.
, ma to zastosowanie tylko wtedy, gdy każda metoda oznaczona@Before
ma unikalną nazwę w hierarchii klas.Na przykład miałem:
public class AbstractFooTest { @Before public void setUpTest() { ... } } public void FooTest extends AbstractFooTest { @Before public void setUpTest() { ... } }
Spodziewałem
AbstractFooTest.setUpTest()
się ucieczki wcześniejFooTest.setUpTest()
, ale tylkoFooTest.setupTest()
został stracony.AbstractFooTest.setUpTest()
nie został w ogóle wezwany.Aby kod działał, należy zmodyfikować go w następujący sposób:
public void FooTest extends AbstractFooTest { @Before public void setUpTest() { super.setUpTest(); ... } }
źródło
@Before
/@After
metody w klasie bazowejfinal
, więc kompilator będzie narzekał, jeśli (przypadkowo) spróbujesz zastąpić je w podklasie.@Before
zaznaczone metody zostaną zignorowane, jeśli podklasa również ma@Before
metodę.@Before
interfejsów mixin, ale także zapobiega ich wykonaniu, jeśli podano OverrideMyślę, że na podstawie dokumentacji
@Before
i@After
właściwym wnioskiem jest nadanie metodom unikalnych nazw. W swoich testach używam następującego wzoru: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"); } }
i
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"); } }
dać w rezultacie
Zaleta tego podejścia: Użytkownicy klasy AbstractBaseTest nie mogą przez przypadek przesłonić metod setUp / tearDown. Jeśli chcą, muszą znać dokładną nazwę i mogą to zrobić.
(Niewielka) wada tego podejścia: Użytkownicy nie widzą, że coś dzieje się przed lub po ich setUp / tearDown. Muszą wiedzieć, że te rzeczy są dostarczane przez klasę abstrakcyjną. Ale zakładam, że to jest powód, dla którego używają klasy abstrakcyjnej
źródło
Jeśli zmienisz sytuację, możesz zadeklarować abstrakcję swojej klasy bazowej, a potomkowie zadeklarują metody setUp i tearDown (bez adnotacji), które są wywoływane w metodach setUp i tearDown klasy bazowej z adnotacjami.
źródło
Możesz użyć
@BeforeClass
adnotacji, aby upewnić się, żesetup()
zawsze jest wywoływana jako pierwsza. Podobnie, możesz użyć@AfterClass
adnotacji, aby upewnić się, żetearDown()
zawsze nazywana jest ostatnią.Zwykle nie jest to zalecane, ale jest obsługiwane .
Nie jest to dokładnie to, czego chcesz - ale zasadniczo utrzyma połączenie z bazą danych otwarte przez cały czas trwania testów, a następnie zamknie je raz na zawsze na końcu.
źródło
setupDB()
icloseDB()
zaznaczenie ich za pomocą@BeforeClass
i@AfterClass
oraz zastąpienie metod przed / posetup()
itearDown()
@BeforeClass
i@AfterClass
muszą być statyczne. A co w przypadku, gdy chcemy użyć zmiennych instancji wewnątrz tych metod?@BeforeClass
z Powermock: działa tylko podczas pierwszego uruchomienia testowego. Zobacz ten numer: github.com/powermock/powermock/issues/398To nie jest odpowiedź na pytanie sloganowe, ale jest to odpowiedź na problemy wymienione w treści pytania. Zamiast używać @Before lub @After, przyjrzyj się używaniu @ org.junit.Rule, ponieważ zapewnia to większą elastyczność. Zasoby zewnętrzne (od 4.7) to reguła, którą będziesz najbardziej zainteresowany, jeśli zarządzasz połączeniami. Ponadto, jeśli chcesz mieć gwarantowaną kolejność wykonywania swoich reguł, użyj RuleChain (od 4.10). Sądzę, że wszystkie z nich były dostępne, gdy zadano to pytanie. Poniższy przykład kodu jest kopiowany z javadoców ExternalResource.
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); } }
źródło