Применяйте "@Rule" после каждого "@Test" и перед каждым "@After" в JUnit

У меня есть набор тестов, где я выхожу из системы в @After и закрытие браузера в @AfterClass, Я пытаюсь использовать @Rule сделать скриншот неудачного теста, используя Selenium для каждого метода тестирования. Я проверил вручную, что @Rule работает только перед каждым @Before но я хочу настроить его после @Test и раньше @After, Я не мог найти простое решение. Любая помощь будет оценена.

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;
    }    
}               

Я получаю вывод

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

3 ответа

Решение

Из-за способа настройки правил вы не можете иметь правило, которое следует после @before или before @after. Вы можете думать о правилах, как оболочки, которые вы помещаете в метод испытаний. Первая оболочка, которую нужно запустить - это @before/@after. После этого применяются правила @rules.

Быстрый способ сделать то, что вы хотите сделать, это вообще избежать @After. Правило может быть создано таким образом, чтобы в случае сбоя метода он делал снимок экрана, а затем выполнял ваш после кода. Это не так красиво, как @After, но это работает. (также я реализовал TestRule, потому что MethodRule устарел).

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);

        }


    }
}

Вся работа правила делается в заявлении. Org.junit.runners.model.Statement - это класс, представляющий пакет кода. Поэтому здесь метод apply получает пакет кода, который вы помещаете в оболочку. Apply возвращает ваш оператор, который выполняет пакет кода, который вы ему дали, и окружает его оператором try/catch для отлова ошибок метода.

Выход для этого метода:

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

Надеюсь это поможет!

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
                }
            }
        };
    }
}

Как насчет использования правила ExternalResource?
Похоже, что это может дать вам достаточно гибкости в том, что вам нужно.
И если это не совсем то, что вам нужно, взгляните на исходный код внешнего ресурса.
Вполне понятно, как реализовать, например, правило, которое будет работать только после вызова теста.

Другие вопросы по тегам