Apply '@Rule' after each '@Test' and before each '@After' in JUnit
Because of the way that rules are set up, you can't have a rule that comes after @before or before @after. You can think of rules like shells that you put on the test method. The first shell to go on is @before/@after. Thereafter the @rules are applied.
A quick way to do what you want to do is to avoid @After altogether. A rule can be created so that it will take a screenshot if a method fails and then execute yours after the code. It isn't quite as pretty as @After, but it works. (also I implemented TestRule because MethodRule has been depreciated).
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); } }}
All the work of the rule is done in a statement. A org.junit.runners.model.Statement is a class that represents a bundle of code. So here the apply method receives the bundle of code that you are putting a shell around. Apply returns your statement that executes the bundle of code that you gave it and surrounds it with a try/catch statement to catch the method failures.
The output for this method is:
I am beforeclassI am beforeI am a TestI am afterI am beforeI am a Failed TestI take a ScreenshotI am afterI am afterclass
Hope this helps!
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 } } }; }}
What about using the ExternalResource rule ?
Looks like you it can give you enough flexibility to what you need.
And if this is not exactly what you need, take a look at the source code of external resource.
It's quite understandble how to implement a rule for example that will work only after the test invocation.