如何在 Espresso 中重新运行失败的测试? - 头脑风暴
Posted
技术标签:
【中文标题】如何在 Espresso 中重新运行失败的测试? - 头脑风暴【英文标题】:How re-run failed test in Espresso? - brainstorming 【发布时间】:2016-06-25 14:07:45 【问题描述】:我想弄清楚如何使用 Espresso 重新运行失败的测试。我认为这比常见的 JUnit 测试用例要复杂一些,因为您需要从测试开始之前恢复应用程序中的状态。
我的方法是创建自己的 ActivityTestRule,所以我只是从这个类中复制了整个代码并将其命名为 MyActivityTestRule。
在仪器测试的情况下,规则还需要我们希望如何开始我们的活动的信息。我更喜欢自己启动它,而不是有环境为我做它。比如:
@Rule
public MyActivityTestRule<ActivityToStartWith> activityRule = new MyActivityTestRule<>(
ActivityToStartWith.class, true, false
);
所以我也在@Before注释方法中启动我的活动:
@Before
public void setUp() throws Exception
activityRule.launchActivity(new Intent());
并在@After注解方法中进行清理:
@After
public void tearDown() throws Exception
cleanUpDataBaseAfterTest();
returnToStartingActivity(activityRule);
这些方法 - setUp()、tearDown() 在每次测试运行之前/之后都必须调用 - 以确保测试启动期间的应用状态正确。
到目前为止,我在 MyActivityTestRule 中做了很少的修改。首先是应用方法的变化:
@Override
public Statement apply(final Statement base, Description description)
return new ActivityStatement(super.apply(base, description));
这对我来说是一个未知的事情,因为放置在 ActivityTestRule 中的 ActivityStatement 具有 super.apply 方法,因此它还将测试语句包装在 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();
不管我用我的测试做什么,我永远无法创建 case mRunOnUiThread boolean 为真。如果在我的测试用例中存在带有注释 @UiThreadTest 的测试,那将是真的 - 或者这就是我从代码中理解的。但它永远不会发生,我不使用类似的东西,所以我决定忽略这个 UiThreadStatement 并将 MyActivityTestRule 更改为:
@Override
public Statement apply(final Statement base, Description description)
return new ActivityStatement(base);
我的测试用例运行没有任何问题。多亏了我剩下的一切 - 围绕 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();
一般来说,只有在我将 ActivityTestRule 构造函数的第三个参数设置为 true 时才会调用 launchActivity。但是我自己在 setUp() 中启动测试,所以它永远不会发生。
据我了解,mBase.evaluate() 在 @Test 注释方法中运行我的代码。它还会在 throwable 被抛出期间停止测试用例。这意味着我可以抓住它并重新启动它——就像那里建议的那样: How to Re-run failed JUnit tests immediately?
好吧,我做了类似的事情:
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();
// if you reach this lane that means evaluate passed
// and you don't need to do the next run
break;
catch (Throwable t)
// save first throwable if occurred
if (throwable == null)
throwable = t;
// my try that didn't work
launchActivity(testInitialIntent);
// I've returned test to starting screen but
// mBase.envaluate() didn't make it run again
// it would be cool now to:
// - invoke @After
// - finish current activity
// - invoke @Before again and start activity
// - mBase.evaluate() should restart @Test on activity started again by @Before
finishActivity();
afterActivityFinished();
// if 1st try fail but 2nd passes inform me still that there was error
if (throwable != null)
throw throwable;
所以 catch 块中的那些 cmets 是我不知道该怎么做的部分。我尝试根据我在 setUp() 中使用的意图执行 launchActivity 第一次运行测试。但是 mBase.evaluate() 并没有让它做出反应(测试用例没有再次出现) - 什么也没发生 + 我认为它不会真正拯救我。我缺少我在@SetUp 中所做的一些启动,它没有被再次调用。我真的很想找到一种方法来重新正确地重新启动整个测试生命周期@Before @Test @After。也许从代码中调用 Instrumentation 或 TestRunner。
有什么想法可以做到吗?
【问题讨论】:
有解决办法吗? 如果碰巧您正在使用 firebase 测试实验室,您可以为此使用标志“--num-flaky-test-attempts 2”(重试两次)。事实证明,将重复行为放入测试本身太不稳定/难以排除故障,因此我不鼓励这样做。 【参考方案1】:答案超级简单。只需确保将 espresso 测试升级到 Junit 4,然后查看 this answer
【讨论】:
我试图将evaluate()
放入循环中,但发生了什么 -> @Test 代码块再次执行,但设备没有反应。它在 Espresso 上对你有用吗——不是常见的 Junit 测试? (我使用的是 Junit 4.12 版)
是的,我在我们的 Espresso 测试中实现了这一点,效果很好。这可能是您设置测试的方式,这就是问题所在。您能否发布一个示例测试的要点,其中还包括我链接到的答案中实施的重试规则?
嘿,非常感谢您提供的信息。目前我真的很忙,但我肯定会尽快检查它,因为我对此非常感兴趣,如果它有效,我会给你反馈或提供一些代码,以便我们检查我做错了什么:)【参考方案2】:
您可以使用https://github.com/AdevintaSpain/Barista 库重新运行失败的测试。
您可以在此处查看有关处理易碎测试的更多详细信息:https://github.com/AdevintaSpain/Barista#dealing-with-flaky-tests
【讨论】:
以上是关于如何在 Espresso 中重新运行失败的测试? - 头脑风暴的主要内容,如果未能解决你的问题,请参考以下文章
Android - Espresso:为每次测试重新创建活动
Espresso:如何测试 SwipeRefreshLayout?
如何使用 android studio 运行单个 espresso 测试功能?