2015-04-28 12 views

Odpowiedz

6

Oto jak ja to robię w tej chwili:

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

Moja baza Espresso klasa Test ustawia niestandardową FailureHandler (lubię przy użyciu klasy bazowej posiadać wiele innych wspólnych kierunkowym):

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

... i tutaj jest nieco zmodyfikowany kod screenshot z the Gist posted by Square:

/** 
* 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)); 
    } 
} 

Chciałbym znaleźć sposób na uniknięcie dzwonienia pod numer setUpFailureHandler() w każdym teście - proszę dać mi znać, jeśli masz dobry pomysł, jak tego uniknąć!

2

Możesz spróbować ustawić to w swojej podklasie ActivityRule. Coś jak

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

nie jestem pewien, że i testMethodNametestClassName będzie zawsze być poprawne. Sposób, w jaki je dostaję, wydaje się bardzo delikatny, ale nie mogłem wymyślić lepszego sposobu.

6

W oparciu o powyższe podejście @ Eric oraz z ActivityTestRule możemy uzyskać aktualną nazwę metody testowania i nazwę klasy testowej z obiektu description po wywołaniu funkcji . Nadrzędnymi funkcję ubiegania się ten

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 ... */ 
} 

byłem w stanie podjąć zrzut ekranu z właściwej metody badawczej i klasy testowej dzięki czemu może być prawidłowo zintegrowane końcowego testu łyżką. I pamiętaj, aby użyć biegacza JUnit4, dodając do swojej klasy testowej:

@RunWith(AndroidJUnit4.class) 

.

+1

Użycie SpoonScreenshotAction samo wywoła funkcję FailureHandler Espresso, gdy np. nie znaleziono aktywności. Jest to złe, ponieważ prowadzi to do nieskończonej pętli, a zatem do błędu OutOfMemory. Lepiej użyj Spoon.screenshot() bezpośrednio: '' Łyżka.screenshot (getActivity(), tag, testClassName, testMethodName); '' zamiast SpoonScreenshotAction.perform(); – sebokopter

0

Zastąpienie domyślnego komunikatu FailureHandler espresso niestandardowym umożliwia dodatkową obsługę błędów, np. zrzut ekranu:

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

Również trzeba rzucić wyjątek niestandardową w konfiguracji testowej i zniszczeń:

@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())); 
    } 

Można to wykorzystać w teście espresso jak:

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

Proszę spojrzeć na przykład oficjalny Android CustomFailure:
Click here for the official example

Click here for another example

Powiązane problemy