Apply '@Rule' after each '@Test' and before each '@After' in JUnit

后端 未结 3 1068
隐瞒了意图╮
隐瞒了意图╮ 2020-12-06 05:10

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

相关标签:
3条回答
  • 2020-12-06 05:26
    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
                    }
                }
            };
        }
    }
    
    0 讨论(0)
  • 2020-12-06 05:34

    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!

    0 讨论(0)
  • 2020-12-06 05:37

    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.

    0 讨论(0)
提交回复
热议问题