如何让勺子为浓缩咖啡测试截图



我一直在尝试按照说明获取 Spoon 1.1.14 来截取失败的浓缩咖啡测试的屏幕截图。

使用自定义 Espresso FailureHandler 配置此功能的最佳方法是什么?

这是我目前的做法:

public class MainScreenTest extends BaseStatelessBlackBoxEspressoTest<LaunchActivity> {
    public MainScreenTest() {
        super(LaunchActivity.class);
    }
    public void testMainScreen() {
        // Unfortunately this must be explicitly called in each test :-(
        setUpFailureHandler();
        onView(withId(R.id.main_circle)).
                check(matches(isDisplayed()));
    }
}

我的基本 Espresso 测试类设置了自定义 FailureHandler(我喜欢使用基类来保存许多其他常见代码):

public abstract class BaseStatelessBlackBoxEspressoTest<T extends Activity> extends BaseBlackBoxTest<T> {
    public BaseStatelessBlackBoxEspressoTest(Class clazz) {
        super(clazz);
    }
    @Before
    public void setUp() throws Exception {
        super.setUp();
        getActivity();
    }
    public void setUpFailureHandler() {
        // Get the test class and method.  These have to match those of the test
        // being run, otherwise the screenshot will not be displayed in the Spoon 
        // HTML output.  We cannot call this code directly in setUp, because at 
        // that point the current test method is not yet in the stack.
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        String testClass = trace[3].getClassName();
        String testMethod = trace[3].getMethodName();
        Espresso.setFailureHandler(new CustomFailureHandler(
                getInstrumentation().getTargetContext(),
                testClass,
                testMethod));
    }
    private static class CustomFailureHandler implements FailureHandler {
        private final FailureHandler mDelegate;
        private String mClassName;
        private String mMethodName;
        public CustomFailureHandler(Context targetContext, String className, String methodName) {
            mDelegate = new DefaultFailureHandler(targetContext);
            mClassName = className;
            mMethodName = methodName;
        }
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher) {
            try {
                mDelegate.handle(error, viewMatcher);
            } catch (Exception e) {
                SpoonScreenshotAction.perform("espresso_assertion_failed", mClassName, mMethodName);
                throw e;
            }
        }
    }
}

。这是Square发布的Gist中略微修改的屏幕截图代码:

/**
 * Source: https://github.com/square/spoon/issues/214#issuecomment-81979248
 */
public final class SpoonScreenshotAction implements ViewAction {
    private final String tag;
    private final String testClass;
    private final String testMethod;
    public SpoonScreenshotAction(String tag, String testClass, String testMethod) {
        this.tag = tag;
        this.testClass = testClass;
        this.testMethod = testMethod;
    }
    @Override
    public Matcher<View> getConstraints() {
        return Matchers.anything();
    }
    @Override
    public String getDescription() {
        return "Taking a screenshot using spoon.";
    }
    @Override
    public void perform(UiController uiController, View view) {
        Spoon.screenshot(getActivity(view), tag, testClass, testMethod);
    }
    private static Activity getActivity(View view) {
        Context context = view.getContext();
        while (!(context instanceof Activity)) {
            if (context instanceof ContextWrapper) {
                context = ((ContextWrapper) context).getBaseContext();
            } else {
                throw new IllegalStateException("Got a context of class "
                        + context.getClass()
                        + " and I don't know how to get the Activity from it");
            }
        }
        return (Activity) context;
    }    
    public static void perform(String tag, String className, String methodName) {
        onView(isRoot()).perform(new SpoonScreenshotAction(tag, className, methodName));
    }
}

我很想找到一种方法来避免在每次测试中都打电话给setUpFailureHandler() - 如果您对如何避免这种情况有好主意,请告诉我!

基于上述@Eric的方法,并使用 ActivityTestRule,我们可以在调用函数时从对象description获取当前的测试方法名称和测试类apply()名称。 通过像这样覆盖应用函数

public class MyActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
  @Override
  public Statement apply(Statement base, Description description) {
    String testClassName = description.getClassName();
    String testMethodName = description.getMethodName();
    Context context =  InstrumentationRegistry.getTargetContext();
    Espresso.setFailureHandler(new FailureHandler() {
      @Override public void handle(Throwable throwable, Matcher<View> matcher) {
        SpoonScreenshotAction.perform("failure", testClassName, testMethodName);
        new DefaultFailureHandler(context).handle(throwable, matcher);
        }
    });
    return super.apply(base, description);
  }
  /* ... other useful things ... */
}

我能够使用正确的测试方法和测试类截取屏幕截图,以便可以将其正确集成到最终的 Spoon 测试报告中。 并记住使用 JUnit4 运行器,方法是添加

@RunWith(AndroidJUnit4.class)

到您的测试类。

您可以尝试在 ActivityRule 的子类中设置它。 类似的东西

return new Statement() {
  @Override public void evaluate() throws Throwable {
    final String testClassName = description.getTestClass().getSimpleName();
    final String testMethodName = description.getMethodName();
    Instrumentation instrumentation = fetchInstrumentation();
    Context context = instrumentation.getTargetContext();
    Espresso.setFailureHandler(new FailureHandler() {
      @Override public void handle(Throwable throwable, Matcher<View> matcher) {
        SpoonScreenshotAction.perform("failure", testClassName, testMethodName);
        new DefaultFailureHandler(context).handle(throwable, matcher);
      }
    });
    base.evaluate();
  }
} 

我不确定testClassNametestMethodName是否永远正确。 我获取这些的方式似乎非常脆弱,但我想不出更好的方法。

将 Espresso 的默认 FailureHandler 替换为自定义处理程序允许额外的错误处理,例如截取屏幕截图:

private static class CustomFailureHandler implements FailureHandler {
@Override
  public void handle(Throwable error, Matcher<View> viewMatcher) {
    throw new MySpecialException(error);
}
  }
  private static class MySpecialException extends RuntimeException {
  MySpecialException(Throwable cause) {
     super(cause);
    }
}

此外,还需要在测试设置和拆卸中引发自定义异常:

 @Override
   public void setUp() throws Exception {
   super.setUp();
   getActivity();
   setFailureHandler(new CustomFailureHandler());
  }
@Override
  public void tearDown() throws Exception {
  super.tearDown();
  Espresso.setFailureHandler(new DefaultFailureHandler(getTargetContext()));
  }

您可以在浓缩咖啡测试中使用它,例如:

public void testWithCustomFailureHandler() {
  try {
  onView(withText("does not exist")).perform(click());
} catch (MySpecialException expected) {
  Log.e(TAG, "Special exception is special and expected: ", expected);
  }
}

请看 Android 官方的 CustomFailure 示例:
点击这里查看官方示例

单击此处查看另一个示例

最新更新