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.