Apply '@Rule' after each '@Test' and before each '@After' in JUnit
java, junit-rule, junit4, selenium, webdriver
Solution
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 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
Hope this helps!
Problem
I have a test suite where I am logging out of the system in `@After` and closing the browser in `@AfterClass`. I am trying to use `@Rule` to take failed test screenshot using Selenium for every test method. I checked manually that `@Rule` only runs before every `@Before` but I want to set it up after `@Test` and before `@After`. I couldn't find out simple solution. Any help will be appreciated. ``` 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; } } ``` The output I am getting is ``` 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 ```