principes, librairies, impactdeptinfo.unice.fr/~renevier/ancien/poo-2015/cours... · •flux usuels...
TRANSCRIPT
PO
O 2
01
4-2
01
5
Principes, Librairies, Impact
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 02 / 29
• Un framework de test intégré dans le sdk– JUNIT 3 pour l’extension pour android– Travail en cours pour JUNIT 4
• Junit « normal » pour le java « normal »– Tout ce qui n’est pas graphique ou qui n’a pas besoin de
l’environnement « android » se teste comme au S5, avec JUnit 3 (ou 4)
• Suites de tests dans des environnements semblables aux applications
• Intégration dans l’IDE• Doc :
https://developer.android.com/tools/testing/index.html
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 03 / 29
• Ecrire des sous-classes de TestCase
• Un TestCase peut définir un nombre quelconque de méthodes testXxx()
• Pour vérifier les résultats attendus (écrire des oracles !), il faut appeler une
des nombreuses variantes de méthodes assertYYY () fournies
– assertTrue(String message, boolean test), assertFalse(…)
– assertEquals(…) : test d’égalité avec equals
– assertSame(…), assertNotSame(…) : tests d’égalité de référence
– assertNull(…), assertNotNull(…)
– Fail(…) : pour lever directement une AssertionFailedError
– Surcharge sur certaines méthodes pour les différentes types de base
d’après un cours de Philippe Collet
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 04 / 29
MonTest
Une méthode testXXX() par testd’après un cours de Philippe Collet
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 05 / 29
package junit4;
import calc.Calculator;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
private static Calculator calculator =
new Calculator();
@Before
public void clearCalculator() {
calculator.clear();
}
@Test
public void add() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(), 2);
}
package junit3;
import calc.Calculator;
import junit.framework.TestCase;
public class CalculatorTest extends
TestCase {
private static Calculator calculator =
new Calculator();
protected void setUp() {
calculator.clear();
}
public void testAdd() {
calculator.add(1);
calculator.add(1);
assertEquals(calculator.getResult(),
2);
}
d’après un cours de Philippe Collet
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 06 / 29
6
@Test(expected =
ArithmeticException.class)
public void divideByZero() {
calculator.divide(0);
}
@Ignore("not ready yet")
@Test
public void multiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),
100);
}
}
public void testDivideByZero() {
try {
calculator.divide(0);
fail();
} catch (ArithmeticException e) {
}
}
public void notReadyYetTestMultiply() {
calculator.add(10);
calculator.multiply(10);
assertEquals(calculator.getResult(),
100);
}
}
d’après un cours de Philippe Collet
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 07 / 29
• Création d’un « package »
• En fait même package que l’application
• Mais dans un dossier « androidTest » à côté de « Main » dans <projet>/app/src
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 08 / 29
• AndroidTestCase– Test général avec l’environnement Android (Instrumentation), mais pas graphique– Instrumentation : accès à l’environnement Android
• Test d’activité– ActivityInstrumentationTestCase2
Pour tester les activités dans un “contexte” habituelpossibilité pour de mock objects pour les Intents (communications entre application)
– ActivityUnitTestCaseIsolation (hors contexte)Mock Object pour contexte et d’application
– SingleLaunchActivityTestCaseinvokes setUp() and tearDown() only onceno mock objects.
• Et d’autres… (service, content provider (save & restore date, UI Monkey test)
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 09 / 29
Recours à une libraire, Robotium
Utilisation de l’extérieur (à la place d’un « utilisateur »)
Pas d’impact sur l’architecture
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 10 / 29
• Un exemple dans le sdk… Spinner(Activity)Test• ActivityInstrumentationTestCase2• Tests et le UI Thread • Certaines actions interdites en dehors du thread graphique
– Exécuter une action dans le processus graphique (clic…)Méthode runOnUiThreadParamètre : un Runnable• seul le processus graphique peut modifier l’interface graphique
– synchronisation (attente actions “graphique” terminées) getInstrumentation().waitForIdleSync() ;
– Attention aux blocages ou à la gestion de la file d’attente des événements
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 11 / 29
public void testAjout() {
// focus sur le edittext, normalement par defaut... maisrunOnUI(new Runnable() {
public void run() {nouveau.requestFocus();}
});
for (int i = 0; i < taches.length; i++) {getInstrumentation().sendStringSync(taches[i]);// ajouter.callOnClick(); // provoque
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
runOnUI(clickOnView(ajouter));assertEquals("nombre de vue sous liste", i+1, liste.getChildCount())}
} private void runOnUI(Runnable r) {
todo.runOnUiThread(r);
getInstrumentation().waitForIdleSync();
}
private Runnable clickOnView(final View v) {
return new Runnable() {
public void run() {
v.callOnClick();
}
};
}
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 12 / 29
public void testChangement() {
testAjout();
// on recupere les Items
View [] items = getChilds(liste);
int nb_items = items.length;
// on memorise le premier apres les ajouts... qui sera deplace en dernier en n-1 etapes
View first = items[0];
ImageButton down = (ImageButton) first.findViewWithTag("down");
for(int i = 0; i < nb_items-1; i++) {
assertEquals("nombre de vue sous liste", taches.length, items.length);
// on deplace le 1er au dernier
runOnUI(clickOnView(down));
// on recupere a nouveau les Item
items = getChilds(liste);
assertEquals("l'item est en "+(i+1), items[i+1], first);
}
}
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 13 / 29
• Rotation => création à nouveau…– Donc… nouvelle activité, nouveaux objets…
• Les objets initiaux (setUp) sont périmés…
• Pour récupérer la nouvelle instance… il faut passer par Robotium (plus simple)– https://code.google.com/p/robotium/
– S’ajoute au projet • Mettre le jar dans le dossier libs qui est dans le dossier app
• Ou utilisation de Gradle / Maven Central Search
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 14 / 29
• menu File > Project Structure
• Bouton +
• http://search.maven.org/
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 15 / 29
• Changement d’orientation avec Robotium
// getting and changing the orientationint orientation = todo.getRequestedOrientation();if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
mSolo.setActivityOrientation(Solo.LANDSCAPE);else mSolo.setActivityOrientation(Solo.PORTRAIT);
• Synchro sur la création de l’activité et qu’elle ne fasse plus rien
mSolo.waitForActivity(TodoList.class);getInstrumentation().waitForIdleSync();
• On peut récupérer les nouveaux objets...
//this will return new activitytodo = (TodoList) mSolo.getCurrentActivity();
// updating all references...liste = (LinearLayout) todo.findViewById(id.liste);
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 16 / 29
• Rotation (re-création des objets)
• Gestion des threads (graphique vs le reste)
• Incorporation de ressources dans les classes– Si intégration trop couplée, cela ne sera pas
« mockable »
– Cas des listeners (accéléromètres, gps, etc.)
– Cas d’accès à des fichiers de stockage, de bd, etc.
– Cas d’accès à des « intents » (reconnaissance vocale de google par exemple)
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 17 / 29
Impact sur l’architecture : isoler ce qui doit être testé unitairement
Conséquences : modularité, contraintes sur l’architecture
Illustration avec la sauvegarde locale
Recours à la librairie Mockito
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 18 / 29
• Ouverture de flux :
– Méthode de la classe Context
– FileInputStream avec openFileInput(Nom)
– FileOutputStream avec openFileOutput(Nom, Mode)
• Mode = Context.MODE_PRIVATE (écrasement)
• Mode = Context.MODE_APPEND (concaténation)
– /data/data/<app>/files
• Flux usuels de java
Tip: If you want to save a static file in your
application at compile time, save the file in yourproject res/raw/directory. You can open it
with openRawResource(), passing
the R.raw.<filename> resource ID. This method
returns an InputStream that you can use to read
the file (but you cannot write to the original file).
[http://developer.android.com/]
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 19 / 29
• Permission : – <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />– READ_EXTERNAL_STORAGE
• context. getExternalFilesDir(null)– http://developer.android.com/reference/android/content/Context.ht
ml#getExternalFilesDir(java.lang.String)– Starting in KITKAT, no permissions are required to read or write to the
returned path; it's always accessible to the calling app. This only applies to paths generated for package name of the calling application. To access paths belonging to other packages, WRITE_EXTERNAL_STORAGE and/or READ_EXTERNAL_STORAGE are required.
• new File(ctx.getExternalFilesDir(null), "nomdufichier")– /sdcard/Android/data/pakage.de.application/files/nomdufichier
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 20 / 29
• Chargement / Sauvegare de notes dans la TodoList
• Chargement… dans le onCreate(pour être dès le début)– Code non accessible sans le
onCreate…– Complexification du test…– … qui n’est plus unitaire
• Isolation– Testable séparément– Mock possible– Choix :
• paramètrage• Retour
• Gain : modularité
Activity
onCreate
Load
Activity
onCreate
load()
invokes
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 21 / 29
• Création d’Activity « lourde » de sens
• Mock « static » impossible ?– Cas d’utilitaires…– Powermock a priori non
compatible…
• Solution : utilisation d’un champ static– Un mock peut être fait– Ce mock peut être affecté
avant la création de l’objet
• Méthode load() pour tester utilisation sans l’ensemble de l’activité
Activity
onCreate
load()uses mySave
invokes
static Save mySave = usualValue ;
Save
Mock of Save
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 22 / 29
• Chargement– paramètre : le Context
• Nécessaire pour ouvrir le flux
– retour : un tableau de valeurs • séparation lecture des
données / création d’objet (fragment) en partie graphique
• AndroidTestCase– Fournit l’environnement
Android– Possibilité de setContext
• MockContext– Les méthodes par défaut
lève des exceptions– Permet de remplacer les
méthodes de communication avec l’extérieur
– le test devient indépendant au Context
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 23 / 29
• Dans le setUp du Test, capture du contexte initialinitialContext = getContext();
• Appel à setContextMyMockContext context = new MyMockContext(R.raw.storage_input_test);
setContext(context);
• Chargé un fichier pour le test, placé dans res/rawprotected class MyMockContext extends MockContext {
/** the file must not be compressed... **/
int idRaw;
MyMockContext() { this(R.raw.storage_input_test); }
MyMockContext(int idRaw) {
super();
this.idRaw = idRaw;
}
public FileInputStream openFileInput(String name) {
AssetFileDescriptor afd = initialContext.getResources().openRawResourceFd(idRaw);
FileInputStream res = null;
res = new FileInputStream(afd.getFileDescriptor());
return res;
}
}
Voir aussi RenamingDelegatingContext
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 24 / 29
• Comment vérifier qu’on écrit (sauvegarde) ce qu’il faut ?
– vérifier que c’est au bon format…
– Avec les bonnes valeurs…
• Il faudrait pouvoir recevoir le résultat du flux de sortie (FileOutputStream)
• => Mock avec Mockito
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 25 / 29
• Téléchargement de jar ou via Gradle / MavenCentral Search
• Nécessite deux autres libraires – Car format .class ≠ .dex (Dalvik Executable format)– dexmaker + dexmaker-mockito– Initialisation (avant de faire un mock)
System.setProperty("dexmaker.dexcache", initialContext.getCacheDir().getPath());
• initialContext– getContext() dans un AndroidTestCase– getInstrumentation().getTargetContext() dans un
ActivityInstrumentationTestCase2
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 26 / 29
public FileOutputStream openFileOutput(String name, int mode) {
FileOutputStream res = mock(FileOutputStream.class);
try {
// objectif : simuler l’écriture
// récupérer les « write » et stocker ce qui
// est écrit dans une String « writte », champ de
// classe de MyMockContext
// mock write(int)
// mock write(byte[])
// mock write(byte[], int, int)
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 27 / 29
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
byte b = ((Byte) args[0]).byteValue();
written = written + Byte.toString(b);
return null;
}}).when(res).write(anyInt());
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
byte[] b = (byte[]) args[0];
try {
written = written + new String(b, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}}).when(res).write(Matchers.<byte[]>any());
• public voidwrite(byte[] b)
• Récupérer la valeur (args[0])
• Cast en String et concaténation
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 28 / 29
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
byte[] bytes = (byte[]) args[0];
int start = ((Integer) args[1]).intValue();
int len = ((Integer) args[2]).intValue();
bytes = Arrays.copyOfRange(bytes, start, start+len);
try {
written = written + new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}}).when(res).write(Matchers.<byte[]>any(), anyInt(), anyInt());
Test & Android Tests Graphiques Tests & Architecture Mockito et Android
PO
O 2
01
4-2
01
5
Philippe Renevier Gonin - L3 Informatique Parcours Miage - POO - Android - Semestre 6, 2014-2015 29 / 29
• Test d’écriture :
– Mock du Context (MyMockContext) -> context
– Préparation des objets à sauvegarder
• Mock…
• Oracle : on connait la String à écrire -> expected
– On fait la sauvegarde
• Écriture via le mock du FileOutputStream
– On compare assertEquals("msg...", expected, context.written);
Test & Android Tests Graphiques Tests & Architecture Mockito et Android