Suche…


Einführung

Der Schwerpunkt dieses Dokuments liegt auf der Darstellung von Zielen und Schreibweisen für Android-Benutzeroberflächen- und Integrationstests. Espresso und UIAutomator werden von Google bereitgestellt. Der Fokus sollte sich also auf diese Tools und ihre jeweiligen Verpackungen (z. B. Appium, Löffel usw.) richten.

Syntax

  • Ressource im Leerlauf
  • String getName () - Gibt den Namen der im Leerlauf befindlichen Ressource zurück (wird für die Protokollierung und die Idempotenz der Registrierung verwendet).
  • boolean isIdleNow () - Gibt "true" zurück, wenn sich die Ressource aktuell im Leerlauf befindet.
  • void registerIdleTransitionCallback (IdlingResource.ResourceCallback-Callback) - Registriert die angegebene IdlingResource.ResourceCallback bei der Ressource

Bemerkungen

JUnit-Regeln:

Wie Sie in MockWebServer Beispiel und ActivityTestRule sie alle fallen unter die Kategorie von JUnit Regeln sehen können , die Sie selbst erstellen können , die dann sollte für jeden Test definiert , sein Verhalten @see ausgeführt werden: https://github.com/junit-team/junit4/ Wiki / Regeln

Appium

Parameter

Da bei Parametern einige Probleme auftreten, setzen Sie sie hier, bis der Dokumentationsfehler behoben ist:

Parameter Einzelheiten
KlassenaktivitätKlasse welche Tätigkeit beginnt
initialTouchMode Sollte die Aktivität beim Start in den Touch-Modus versetzt werden: https://android-developers.blogspot.de/2008/12/touch-mode.html
launchActivity true, wenn die Aktivität einmal pro Testmethode gestartet werden soll. Es wird vor der ersten Before-Methode gestartet und nach der letzten After-Methode beendet.

MockWebServer Beispiel

Falls für Ihre Aktivitäten, Fragmente und Benutzeroberflächen Hintergrundverarbeitung erforderlich ist, empfiehlt es sich, einen MockWebServer zu verwenden, der lokal auf einem Android-Gerät ausgeführt wird, das eine geschlossene und testbare Umgebung für Ihre Benutzeroberfläche bietet.

https://github.com/square/okhttp/tree/master/mockwebserver

Der erste Schritt umfasst die Abstufung der Abstufung:

testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'

Schritte zum Ausführen und Verwenden des Mock-Servers sind jetzt:

  • Mock-Serverobjekt erstellen
  • Starten Sie es an einer bestimmten Adresse und einem bestimmten Port (normalerweise localhost: Portnummer).
  • Antworten für spezifische Anfragen in der Warteschlange
  • Test starten

Das wird auf der Github-Seite des Mockwebservers gut erklärt, aber in unserem Fall möchten wir für alle Tests etwas Netteres und Wiederverwendbares, und JUnit-Regeln werden hier gut ins Spiel kommen:

/**
 *JUnit  rule that starts and stops a mock web server for test runner
*/
 public class MockServerRule extends UiThreadTestRule {

 private MockWebServer mServer;

 public static final int MOCK_WEBSERVER_PORT = 8000;

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                startServer();
                try {
                    base.evaluate();
                } finally {
                    stopServer();
                }
            }
        };
    }

    /**
     * Returns the started web server instance
     *
     * @return mock server
     */
    public MockWebServer server() {
        return mServer;
    }

    public void startServer() throws IOException, NoSuchAlgorithmException {
        mServer = new MockWebServer();
        try {
            mServer(MOCK_WEBSERVER_PORT);
        } catch (IOException e) {
            throw new IllegalStateException(e,"mock server start issue");
        }
    }

    public void stopServer() {
        try {
            mServer.shutdown();
        } catch (IOException e) {
            Timber.e(e, "mock server shutdown error”);
        }
    }
}

Nehmen wir nun an, dass wir genau die gleiche Aktivität wie im vorigen Beispiel haben. In diesem Fall wird beim Drücken der Schaltfläche die App beispielsweise etwas vom Netzwerk abrufen: https://someapi.com/name

Dies würde eine Textzeichenfolge zurückgeben, die im Snackbar-Text verkettet würde, z. B. NAME + von Ihnen eingegebener Text.

/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);

    //start mock web server
    @Rule
    public final MockServerRule mMockServerRule = new MockServerRule();

    @Override
    public void tearDown() throws Exception {
       //same as previous example
    }
    
    @Override
    public void setUp() throws Exception {
       //same as previous example

       **//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
       MyAppConfig.setEndpointURL("http://localhost:8000");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //setup mockweb server
        mMockServerRule.server().setDispatcher(getDispatcher());

        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        //we check is our snackbar showing text from mock webserver plus the one we typed
        onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
    
     /**
     *creates a mock web server dispatcher with prerecorded requests and responses
     **/
    private Dispatcher getDispatcher() {
        final Dispatcher dispatcher = new Dispatcher() {
            @Override
            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                if (request.getPath().equals("/name")){
                    return new MockResponse().setResponseCode(200)
                            .setBody("JazzJackTheRabbit");
                }
                throw new IllegalStateException("no mock set up for " + request.getPath());
            }
        };
        return dispatcher;
    }

Ich würde vorschlagen, den Dispatcher in eine Art Builder zu packen, damit Sie auf einfache Weise neue Antworten für Ihre Bildschirme hinzufügen können. z.B

 return newDispatcherBuilder()
            .withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
            .withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
            .withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());

IdlingResource

Die Leistung von Ressourcen im Leerlauf besteht darin, dass Sie nicht auf die Verarbeitung einiger Apps (Netzwerk, Berechnungen, Animationen usw.) warten müssen, um mit sleep() enden, was Flockigkeit verursacht und / oder den Testlauf verlängert. Die offiziellen Unterlagen finden Sie hier .

Implementierung

Bei der Implementierung der IdlingResource Schnittstelle müssen Sie drei Dinge tun:

  • getName() - Gibt den Namen Ihrer im Leerlauf befindlichen Ressource zurück.
  • isIdleNow() - Überprüft, ob sich das Xyz-Objekt, die Operation usw. momentan im Leerlauf befindet.
  • registerIdleTransitionCallback ( IdlingResource.ResourceCallback Rückruf) - Stellt einen Rückruf IdlingResource.ResourceCallback den Sie aufrufen sollten, wenn Ihr Objekt in den Leerlauf IdlingResource.ResourceCallback .

Jetzt sollten Sie Ihre eigene Logik erstellen und bestimmen, wann sich Ihre App im Leerlauf befindet und wann nicht, da dies von der App abhängt. Nachfolgend finden Sie ein einfaches Beispiel, um zu zeigen, wie es funktioniert. Es gibt andere Beispiele online, aber die Implementierung spezifischer Apps führt zu spezifischen Implementierungen von Ressourcen im Leerlauf.

ANMERKUNGEN

  • Es gab einige Google-Beispiele, bei denen IdlingResources in den Code der App IdlingResources . Mach das nicht. Vermutlich haben sie es dort aufgestellt, um zu zeigen, wie sie arbeiten.
  • Es bleibt Ihnen überlassen, Ihren Code sauber zu halten und ein Prinzip der Verantwortung zu wahren!

Beispiel

Nehmen wir an, Sie haben eine Aktivität, die seltsame Sachen macht und das Laden des Fragments lange Zeit in Anspruch nimmt, sodass Ihre Espresso-Tests fehlschlagen, wenn Sie keine Ressourcen in Ihrem Fragment finden können (Sie sollten ändern, wie und wann Ihre Aktivität erstellt wird) um es zu beschleunigen). Aber um es einfach zu halten, zeigt das folgende Beispiel, wie es aussehen sollte.

Unsere Beispielressource für den Leerlauf würde zwei Objekte erhalten:

  • Das Tag des Fragments, das Sie finden müssen und darauf warten, an die Aktivität angehängt zu werden.
  • Ein FragmentManager- Objekt, das zum Suchen des Fragments verwendet wird.
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;

    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }

    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }

    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}

Nun, da Sie Ihre IdlingResource geschrieben haben, müssen Sie sie irgendwo richtig einsetzen?

Verwendungszweck

Lassen Sie uns die gesamte Testklasseneinrichtung überspringen und schauen Sie einfach, wie ein Testfall aussehen würde:

@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));

    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}

Kombination mit der JUnit-Regel

Das ist nicht zu schwer; Sie können die Leerlaufressource auch in Form einer JUnit-Testregel anwenden. Angenommen, Sie haben ein SDK mit Volley und möchten, dass Espresso darauf wartet. Anstatt jeden Testfall durchzusehen oder im Setup anzuwenden, können Sie eine JUnit-Regel erstellen und einfach Folgendes schreiben:

@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());

Jetzt, da dies ein Beispiel ist, sollte es nicht als selbstverständlich gelten. Der gesamte Code hier ist imaginär und wird nur zu Demonstrationszwecken verwendet:

public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;

    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }

    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow