Aplique ‘@Rule’ após cada ‘@Test’ e antes de cada ‘@After’ no JUnit

Eu tenho um conjunto de testes onde estou logout do sistema em @After e fechando o navegador em @AfterClass . Eu estou tentando usar @Rule para tirar screenshots de teste com falha usando Selenium para cada método de teste. Eu verifiquei manualmente que o @Rule só é executado antes de cada @Before mas quero configurá-lo depois do @Test e antes do @After . Eu não consegui descobrir uma solução simples. Qualquer ajuda será apreciada.

 public class MorgatgeCalculatorTest { @Before public void before(){ System.out.println("I am before"); } @BeforeClass public static void beforeclass(){ System.out.println("I am beforeclass"); } @Test public void test(){ System.out.println("I am Test"); } @Test public void test2(){ System.out.println("I am Test2"); } @After public void after(){ System.out.println("I am after"); } @AfterClass public static void afterclass(){ System.out.println("I am afterclass"); } @Rule ExpensiveExternalResource ExpensiveExternalResource = new ExpensiveExternalResource(); static class ExpensiveExternalResource implements MethodRule { public ExpensiveExternalResource(){ System.out.println("I am rule"); } @Override public Statement apply(Statement arg0, FrameworkMethod arg1, Object arg2) { // TODO Auto-generated method stub return null; } } 

A saída que estou recebendo é

 I am beforeclass I am rule I am before I am Test I am after I am rule I am before I am Test2 I am after I am afterclass 

Devido à maneira como as regras são configuradas, você não pode ter uma regra que venha depois de @antes ou antes de @after. Você pode pensar em regras como shells que você coloca no método de teste. O primeiro shell a continuar é @ before / @ after. Depois disso, as @rules são aplicadas.

Uma maneira rápida de fazer o que você quer fazer é evitar o @After completamente. Uma regra pode ser criada para que seja necessária uma captura de canvas se um método falhar e, em seguida, executar o seu após o código. Não é tão bonito quanto @After, mas funciona. (também implementei o TestRule porque o MethodRule foi depreciado).

 public class MortgageCalculatorTest { @Before public void before(){ System.out.println("I am before"); } @BeforeClass public static void beforeclass(){ System.out.println("I am beforeclass"); } @Test public void test(){ System.out.println("I am a Test"); } @Test public void test2(){ System.out.println("I am a Failed Test"); fail(); } @AfterClass public static void afterclass(){ System.out.println("I am afterclass"); } @Rule public ExpensiveExternalResource ExpensiveExternalResource = new ExpensiveExternalResource(); public static class ExpensiveExternalResource implements TestRule { // public ExpensiveExternalResource(){} public class ExpansiveExternalResourceStatement extends Statement{ private Statement baseStatement; public ExpansiveExternalResourceStatement(Statement b){ baseStatement = b; } @Override public void evaluate() throws Throwable { try{ baseStatement.evaluate(); }catch(Error e){ System.out.println("I take a Screenshot"); throw e; }finally{ after(); } } //Put your after code in this method! public void after(){ System.out.println("I am after"); } } public Statement apply(Statement base, Description description) { return new ExpansiveExternalResourceStatement(base); } } } 

Todo o trabalho da regra é feito em uma declaração. Um org.junit.runners.model.Statement é uma class que representa um pacote de código. Então aqui o método apply recebe o pacote de código que você está colocando em torno de um shell. Apply retorna sua instrução que executa o pacote de código que você deu e a envolve com uma instrução try / catch para capturar as falhas do método.

A saída deste método é:

 I am beforeclass I am before I am a Test I am after I am before I am a Failed Test I take a Screenshot I am after I am afterclass 

Espero que isto ajude!

 public class ScreenshotTestRule implements MethodRule { public Statement apply(final Statement statement, final FrameworkMethod frameworkMethod, final Object o) { return new Statement() { @Override public void evaluate() throws Throwable { try { statement.evaluate(); } catch (Throwable t) { captureScreenshot(frameworkMethod.getName()); throw t; // rethrow to allow the failure to be reported to JUnit } finally { tearDown(); } } public void tearDown() { //logout to the system; } public void captureScreenshot(String fileName) { try { new File("target/surefire-reports/screenshot").mkdirs(); // Insure directory is there FileOutputStream out = new FileOutputStream("target/surefire-reports/screenshot/screenshot-" + fileName + ".png"); out.write(((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES)); out.close(); } catch (Exception e) { // No need to crash the tests if the screenshot fails } } }; } } 

Que tal usar a regra ExternalResource ?
Parece que você pode dar flexibilidade suficiente para o que você precisa.
E se isso não for exatamente o que você precisa, dê uma olhada no código-fonte do recurso externo.
É bastante compreensível como implementar uma regra, por exemplo, que funcionará somente após a invocação do teste.