I am trying to figure out how to re-run failed tests using Espresso. I think this is a little more complicated than a regular JUnit test case, since you need to restore the status in the application before the test starts.
My approach is to create my own ActivityTestRule, so I just copied all the code from this class and named it MyActivityTestRule.
In the case of control and measurement tests, information will also be required on how we want to start our business. I prefer to run it myself, and not have the environment to do it for me. For example:
@Rule public MyActivityTestRule<ActivityToStartWith> activityRule = new MyActivityTestRule<>( ActivityToStartWith.class, true, false );
So, I also start my activity in the @Before annotation method:
@Before public void setUp() throws Exception { activityRule.launchActivity(new Intent()); }
And do the cleanup in @ After annotation method:
@After public void tearDown() throws Exception { cleanUpDataBaseAfterTest(); returnToStartingActivity(activityRule); }
These methods - setUp (), tearDown () are necessary to call before / after each test run - to make sure that the application state during the test run is correct.
Inside MyActivityTestRule, I made a few modifications. First of all, this is a change to the apply method:
@Override public Statement apply(final Statement base, Description description) { return new ActivityStatement(super.apply(base, description)); }
This is for me, but an unknown thing, since an ActivityStatement hosted in an ActivityTestRule has a super.apply method, so it also completes the test statement in UiThreadStatement:
public class UiThreadStatement extends Statement { private final Statement mBase; private final boolean mRunOnUiThread; public UiThreadStatement(Statement base, boolean runOnUiThread) { mBase = base; mRunOnUiThread = runOnUiThread; } @Override public void evaluate() throws Throwable { if (mRunOnUiThread) { final AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); getInstrumentation().runOnMainSync(new Runnable() { public void run() { try { mBase.evaluate(); } catch (Throwable throwable) { exceptionRef.set(throwable); } } }); Throwable throwable = exceptionRef.get(); if (throwable != null) { throw throwable; } } else { mBase.evaluate(); } } }
No, do not do what I do with my trials. I can never create an mRunOnUiThread boolean argument to be true. This will be true if in my test cases there will be tests with the annotation @UiThreadTest - or what I understood from the code. This never happens, but I do not use anything like this, so I decided to ignore this UiThreadStatement and change MyActivityTestRule to:
@Override public Statement apply(final Statement base, Description description) { return new ActivityStatement(base); }
And my test cases work without problems. Thanks to this, all that I have left is a wrap around mBase.evaluate ():
private class ActivityStatement extends Statement { private final Statement mBase; public ActivityStatement(Statement base) { mBase = base; } @Override public void evaluate() throws Throwable { try { if (mLaunchActivity) { mActivity = launchActivity(getActivityIntent()); } mBase.evaluate(); } finally { finishActivity(); afterActivityFinished(); } } }
In the general case, the launch of the Activity will be called only if I set the value of the ActivityTestRule constructor to true in the third parameter. But I run the tests myself in setUp (), so this never happens.
From what I understood, mBase.evaluate () runs my code inside the @Test annotation method. It also stops the test case during the throw. This means that I can catch it and restart it - as suggested there: How do I run failed JUnit tests again?
And all right, I did something like this:
public class ActivityRetryStatement extends Statement { private final Statement mBase; private final int MAX_RUNS = 2; public ActivityRetryStatement(Statement base) { mBase = base; } @Override public void evaluate() throws Throwable { Throwable throwable = null; for (int i = 0; i < MAX_RUNS; i++) { try { mBase.evaluate();
So these comments in the catch block are parts that I don't know how to do. I tried to execute launchActivity according to the intentions that I used in setUp () to run the test for the first time. But mBase.evaluate () didn’t make him react (test case didn’t repeat) - nothing happened +, it would not save me there, I think. I missed some of the initiations I make at @SetUp, it was not called again. I would really like to find a way to properly restart the entire @Before @Test @After testing life cycle again. Perhaps some will call Instrumentation or TestRunner from code.
Any thoughts on how to do this?