NFP121, Cnam/Paris JUnittests pour...

Preview:

Citation preview

NFP1211

NFP121, Cnam/ParisJUnit tests pour Android

Notes sur l’usage de JUnit3 sous Android

Cnam Parisjean-michel Douin, douin au cnam point fr

version en cours

Le lecteur intéressé par un cours sur les tests pourra se référer à celui de l’université de Trente « Software Analysis and Testing » http://selab.fbk.eu/swat/program.ml?lang=en

NFP1212

Avertissement

• Ce support a comme pré-requis :– La connaissance de junit3– Ou la lecture de ce support

• http://jfod.cnam.fr/NFP121/supports/junit3_tests.pd f

NFP1213

Sommaire du « triptyque »

1. Présentation de JUnit, les bases

2. Android et les tests unitaires

3. Assertions et programmation par contrats

NFP1214

Sommaire : Les 3 fichiers

Junit3http://jfod.cnam.fr/NFP121/supports/junit3_tests.pd f

http://jfod.cnam.fr/NFP121/supports/junit3_tests.ja r

Androidhttp://jfod.cnam.fr/NFP121/supports/junit_tests_and roid.pdf

Java et la programmation par contratshttp://jfod.cnam.fr/NFP121/assertions_et_contrats.p df

NFP1215

Sommaire 1)

• Tests et tests unitaires– Outil : junit www.junit.org– Présentation

• Tests d’une application – Architecture MVC : une pile et son IHM

• Tests unitaires de la pile• Tests de plusieurs piles• Tests de l’IHM

– JApplet, JFrame

– Tests en boîte noire– Tests en boîte blanche– Doublures d’objets

• Pertinence des tests– Outil Emma emma.sourceforge.org

NFP1216

Sommaire 2)

• Test unitaires et Android

– Nouvelles Assertions, Nouvelles classes

– Test implicite en boite noire de l’IHM• Tests d’une activity

– Mock Objects

– Tests d’un service

– Tests de la persistance, (d’un « Content Provider »)

NFP1217

Sommaire 3)

– Assertions en Java• Assert, AssertionError• Invariant de représentation, fonction d’abstraction

– Programmation par contrats• Pré-post assertions, invariant de classe• Invariant et variant de boucle, invariant de classe , boucles

– Assertions et héritage• Héritage et pré et post assertions• usage de Jass : source java instrumenté• JML, (JMLUnit), cofoja : usage des annotations

– Initiation à la preuve, • Ce programme respecte les assertions à l’aide d’un démonstrateur• ESC2Java

NFP1218

Bibliographie utilisée

[1] Le cours Software Analysis and Testing de Paolo Tonella , Mariano CeccatoAlessandro Marchetto, Cu Duy Nguyen University of Trento

http://selab.fbk.eu/swat/program.ml?lang=en• Plusieurs figures de ce support sont extraites de cette référence

– http://selab.fbk.eu/swat/slide/3_JUnit.ppt• Lecture préalable indispensable

• http://developer.android.com/resources/tutorials/testing/helloandroid_test.html• http://developer.android.com/guide/topics/testing/index.html• Test Driven Development and Android : http://www.bhecker.com/ap-tdd.ppt

• Emma et Android

– http://stackoverflow.com/questions/2762665/how-to-use-emma-code-coverage-in-android

Le livre utilisé• http://dtmilano.blogspot.com/2008/01/test-driven-development-and-gui-testing.html

Android Application Testing Guide Diego Torres Milano

NFP1219

Rappel : Tests et tests unitaires

• Tests et tests unitaires– Outils : junit3 et junit4

– Le patron Composite• Une feuille est une classe de test• Lecture associée : http://junit.sourceforge.net/doc/cookstour/cookstou r.htm

Objets métiers

Junit ( Low level)junit3 , junit4

NFP12110

Test et Android

• Junit3

• Classes « ordinaires », tests unitaires identiques– assertEquals ….

• Android spécifique:– IHM,– Les services,– La persistance,– Les doublures,– Le « stress » d’une activité.

– Exemple d’une classe proposée par Android :• ActivityInstrumentationTestCase2 extends junit.framework.TestCase

NFP12111

Android L’architecture retenue

• http://developer.android.com/guide/topics/testing/t esting_android.html

NFP12112

IHM : un sommaire

• Une Activity, une IHM

• Accès aux composants graphiques ?

• Accès aux variables d’instance ?

• Comment ?

NFP12113

Applette /Activity, même principe

• A la place d’un utilisateur– Clic, entrée de texte …

• Test avec Android : deux activités– PileActivity– PileActivityTest

NFP12114

Test de l’action empiler

Un scénario de test de l’IHM

1. Écriture d’un nombre2. Cliquer sur le bouton empiler3. Lecture de la zone d’affichage4. Assertions sur le contenu de cette zone

NFP12115

Comment accéder à l’activité à tester ?

• Une classe toute prête:

– ActivityInstrumentationTestCase2<T>• extends junit.framework.TestCase

• Méthodes utiles– getActvity() – getInstrumentation()

– setActivityInitialTouchMode

NFP12116

Le pattern Composite TestCase s’est enrichi

• Extrait de Android Application Testing Guide, Cf. biblio

NFP12117

IntrumentationTestCase

• Extrait de Android Application Testing Guide, Cf. biblio

NFP12118

Test des Activity

• ActivityInstrumentationTestCase2<T>– Prise du contrôle d’une activité

• Marche, arrêt

– Accès aux vues et composants graphiques• Button b = ...

– Génération d’évènements utilisateurs• b.perfomClick();

– Hérite de TestCase• Accès à toute les assertions et bien plus...

• @UiThreadTestpublic void testDeL_IHM() {

NFP12119

Test d’une IHM, des questions se posent

• Accès aux composants graphiques ?– Au sein de l’activité de tests

• Le fichier de ressources de l’activité est accessib le

• Accès aux variables d’instances ?– Par introspection

• Sommes nous toujours en boîte noire …

NFP12120

Rappel : PileActivity, les déclarations

private EditText donnee; private Button boutonEmpiler;private Button boutonDepiler;private EditText sommet;

private TextView contenu ;

Ce sont les attributs de L’IHM

NFP12121

Adéquation Activity/TestActivity

private EditText donnee; private Button boutonEmpiler;private Button boutonDepiler;private EditText sommet;

private PileActivity pileActivity;

private TextView contenu ;

Les attributs de L’IHM deviennent des attributs de l a classe de tests

NFP12122

PileTest … les déclarations

package tp.pile.test;

import tp.pile.PileActivity;import tp.pile.R; // accès aux ressources

public class PileTest extends

ActivityInstrumentationTestCase2<PileActivity> {

public PileTest() {super("pile.tp", PileActivity.class);

}

private PileActivity pileActivity; // L’activité à tester

private EditText donnee; // les composants graphiquesprivate EditText sommet;private TextView contenu;private Button boutonEmpiler;private Button boutonDepiler;

private PileModele<String> pile; // L’instance pile

NFP12123

Affectation des données de l’instance de testsprotected void setUp() throws Exception {

super.setUp();this.pileActivity = this.getActivity();

// par le fichier de ressourcesthis.donnee =

(EditText)pileActivity.findViewById(tp.pile.R.id. donnee);this.sommet =

(EditText)pileActivity.findViewById(tp.pile.R.id. sommet);this.contenu =

(TextView)pileActivity.findViewById(tp.pile.R.id. contenu);this.boutonEmpiler =

(Button)pileActivity.findViewById(tp.pile.R.id. empiler);this.boutonDepiler =

(Button)pileActivity.findViewById(tp.pile.R.id. depiler);

// par introspectionthis.pile = getMember("pileModele", pile, pileActivi ty);

}

NFP12124

Avant le premier test, des pré conditions

• Exécution de la méthode setUp puis

public void testPreconditions() {

assertNotNull(donnee);

assertNotNull(sommet);

assertNotNull(contenu);

assertNotNull(boutonEmpiler);

assertNotNull(boutonDepiler);

assertNotNull(pile);

}

NFP12125

Test action empiler se précise …

Écriture d’un nombreCliquer sur le bouton empiler

Lecture de la zone d’affichageAssertions sur cette zone

donnee.setText(…);boutonEmpiler.performClick();contenu.getText()assertEquals( …)

donneedonneedonneedonneeboutonEmpilerboutonEmpilerboutonEmpilerboutonEmpiler

contenucontenucontenucontenu

NFP12126

Ma première méthode de test

@UiThreadTest

public void testEmpiler() {

donnee.setText("12");

boutonEmpiler.performClick();

donnee.setText("1");

boutonEmpiler.performClick();

String label = contenu.getText().toString();

assertEquals(" Contenu incorrect ???", "[1, 12]",label);

}

NFP12127

Accès aux attributs mêmes privés ?

• Pourquoi faire ?– Sont-ce encore des tests en boites noires ?

• Par introspection

– Field f = obj.getClass().getDeclaredField(name);

– T object = (T)field.get(obj);

NFP12128

Accès aux variables d’instances ?// par introspection, … discussionthis.pile = getMember("pileModele", pile, pileActiv ity);

Avec …@SuppressWarnings("unchecked")public <T> T getMember(String name, T type, Object sourceObj) {

java.lang.reflect.Field field = null;try {

field = sourceObj.getClass().getDeclaredField(name) ;}catch(NoSuchFieldException e) {

fail("L'attribut \"" + memberName + "\" n'existe pas.") ;}field.setAccessible(true);T returnVal = null;

try {returnVal = (T)field.get(sourceObj);

} catch (ClassCastException exc) {fail( name + " n’est pas du bon type.");

} catch (IllegalArgumentException e) {e.printStackTrace();

} catch (IllegalAccessException e) {e.printStackTrace();

}

return returnVal;}

NFP12129

Tests de la variable d’instance …

@UiThreadTest

public void testEmpiler() {

donnee.setText("12");

boutonEmpiler.performClick();

try{

// accès au modèle …

assertEquals ("Le sommet… ???", "12", pile.sommet() );

donnee.setText("1");

boutonEmpiler.performClick();

assertEquals ( "1", pile.sommet() );

}catch(PileVideException e){

fail (" La pile est vide, diantre ! ");

}

}

NFP12130

NFP12131

Architecture des classes possible

• PileActivity ce qui est à tester

• Tests de l’IHM• Tests du modèle• …• Plusieurs paquetages

NFP12132

Architecture : Plusieurs classes

• Plusieurs classes de tests de plusieurs packages– Une seule classe, Cf. TestSuiteBuilder

NFP12133

Résultats junit :

NFP12134

En ligne de commande

• http://developer.android.com/reference/android/test /InstrumentationTestRunner.html

• set path=D:\android-sdk-windows\platform-tools;%PATH%• adb shell am instrument -w cnam.hello.test/android.test.InstrumentationTestRunner•

• cnam.hello.test.HelloAndroidTest:.• Failure in testText:• junit.framework.ComparisonFailure: expected:<... World, HelloActivity!> but was:<..., Android>• at cnam.hello.test.HelloAndroidTest.testText(HelloAndroidTest.java:29)• at java.lang.reflect.Method.invokeNative(Native Method)• at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:204)• at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:194)• at

android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:186)• at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)• at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)• at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520)• at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)

• Test results for InstrumentationTestRunner=..F• Time: 0.667

• FAILURES!!!• Tests run: 2, Failures: 1, Errors: 0

NFP12135

En ligne de commande : la pile

• F:\workspace_android_junit_tests\PileTest>adb shell aminstrument -w all.tests/android.test.InstrumentationT estRunner

• tp.pile.test.ihm.PileTestIHM:........• tp.pile.test.persistance.TestPersistance:....• tp.pile.tests.modele.Pile2Test:...............• tp.pile.tests.modele.Pile2_RepOk_AffTest:..• tp.pile.tests.modele.Pile4Test:...............• tp.pile.tests.modele.PileModeleTest:...• Test results for

InstrumentationTestRunner=.......................... ...............• ......• Time: 5.498

• OK (47 tests)

NFP12136

Ligne de commande Android et Emma

• F:\workspace_android_junit_tests\PileTest>adb shell• $ su• su• # export CLASSPATH=/sdcard/emma.jar• export CLASSPATH=/sdcard/emma.jar

• Pas concluant: génération d’un fichier coverage.ecavec 37 octets

NFP12137

Stress

• Random stress testing – From http://d.android.com/guide/developing/tools/mo nkey.html– When the Monkey runs, it generates events and sends them to the

system. It also watches the system under test and looks for three conditions, which it treats specially:

• If you have constrained the Monkey to run in one or more specific packages, it watches for attempts to navigate to an y other packages, and blocks them.

• If your application crashes or receives any sort of unhandled exception, the Monkey will stop and report the erro r.

• If your application generates an application not responding error, the Monkey will stop and report the error.

adb shell monkey -p cnam.hello -v 500

NFP12138

Stress d’une Activity

NFP12139

Emma et Android

• Emma et Android– http://stackoverflow.com/questions/2762665/how-to-use-emma-

code-coverage-in-android

NFP12140

NFP12141

A voir de près

robotiumIt's like Selenium, but for Android™

http://code.google.com/p/robotium/

NFP12142

À terminer

– public class FunctionalTest extends – ActivityInstrumentationTestCase2<AdvancedJokeList> {– public FunctionalTest() { super ("edu.calpoly.android.lab2", AdvancedJokeList.class);}– protected void setUp() throws Exception { super.setUp(); }– public void testAddJoke() {– ArrayList<Joke> m_arrJokeList = null;– m_arrJokeList = this.retrieveHiddenMember ("m_arrJokeList",– m_arrJokeList,getActivity());– assertEquals("Should be 3 default jokes",m_arrJokeList.size(),3);– getActivity().runOnUiThread (new Runnable() {– public void run () {– AdvancedJokeList theActivity = (AdvancedJokeList)getActivity();– EditText et = (EditText)theActivity.– findViewById (edu.calpoly.android.lab2.R.id.newJokeEditText);– Button bt = (Button)theActivity.– findViewById (edu.calpoly.android.lab2.R.id.addJokeButton);– et.setText ("This is a test joke");– bt.performClick ();– }});– getInstrumentation().waitForIdleSync(); // wait for the request to go through– assertEquals("Should be 4 jokes now",m_arrJokeList.size(),4);– assertEquals("Ensure the joke we added is really there",– m_arrJokeList.get(3).getJoke(),"This is a test joke");– }

NFP12143

NFP12144

AndroidTestCase extends junit.framework.TestCase

• Extrait de Android Application Testing Guide, Cf. biblio