Pro
jet de Licen
ce –2
01
5-2
01
6
Principes, Librairies, Impact
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 02 / 57
• Un framework de test intégré dans le sdk– JUNIT 4
• Deux sortes de tests – Local Tests
• 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 pour projet de développement
– Instrumented Tests• Suites de tests dans des environnements semblables aux applications
• Intégration dans l’IDE• Doc :
https://developer.android.com/tools/testing/index.htmlhttp://developer.android.com/training/testing/unit-testing/index.html
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 03 / 57
• Création d’un « package »• En fait même package que
l’application• Mais dans des dossiers à côté
de « Main » dans <projet>/app/src– app/src/test/java… pour les
« tests locaux »– app/src/androidTest/java…
pour les « tests instrumentés »– Possibilité d’avoir des
ressources (res) spécifique
• (En fonction de la « cible » : Build Variants pour androidstudio < 2)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 04 / 57
Obtenu avec un getResource sur le
ClassLoaderObtenu avec R.raw
avec une configuration du graddle
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 05 / 57
android {
// pour pouvoir lire les fichiers depuis R (R.raw par exemple)
// il ne faut pas qu'ils soient compressés
aaptOptions {
noCompress 'txt'
}
// [...]
defaultConfig {
// [...]
// pour que les tests se lancent en JUnit4
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
// [...]
// pour séparer les ressources pour les tests et celles de l’application
sourceSets {
androidTest {
main.res.srcDirs = ['src/main/res', 'src/androidTest/res']
}
}
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 06 / 57
Tests sans Android
Configurations (app/src/build.graddle)
Intégration de ressources
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 07 / 57
• Des tests pour des classes qui ne dépendent pas de l’environnement Android– Pouvoir les créer sans Context, Instrumentation, etc.
– Pouvoir les utiliser sans Context, Instrumentation, etc.
• Build Variants = Unit Tests pour android studio < 2)
• Dans un dossier app/src/test– app/src/test/java/… (package) pour les tests
– app/src/test/resources pour les fichiers utiles aux tests
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 08 / 57
• (Build Variants => Unit Tests pour android studio < 2)• Dans un dossier src/test
– src/test/java pour les classes– src/test/resources pour les fichiers
•Configuration de build.graddle
// Required -- JUnit 4 frameworktestCompile 'junit:junit:4.12'// Optional -- Mockito frameworktestCompile 'org.mockito:mockito-core:1.+'// ne pas encore utiliser mockito2, provoque// des java.lang.AbstractMethodError: // abstract method not implemented
– édition à la main du fichier de configuration
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 09 / 57
• Pour les imports : org.junit…
– Ne pas prendre les autres propositions
• Pour créer les fichiers :
– Soit manuellement
– Soit avec l’ide :
• Clic droit sur une méthode « Go To test »
• Choisir « Create New Test »
• Fenêtre de création
• Au besoin déplacer / renommer le dossier
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 09 / 57
• Pour les imports : org.junit…
– Ne pas prendre les autres propositions
• Pour créer les fichiers :
– Soit manuellement
– Soit avec l’ide :
• Clic droit sur une méthode « Go To test »
• Choisir « Create New Test »
• Fenêtre de création
• Au besoin déplacer / renommer le dossier
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 09 / 57
• Pour les imports : org.junit…
– Ne pas prendre les autres propositions
• Pour créer les fichiers :
– Soit manuellement
– Soit avec l’ide :
• Clic droit sur une méthode « Go To test »
• Choisir « Create New Test »
• Fenêtre de création
• Au besoin déplacer / renommer le dossier
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 09 / 57
• Pour les imports : org.junit…
– Ne pas prendre les autres propositions
• Pour créer les fichiers :
– Soit manuellement
– Soit avec l’ide :
• Clic droit sur une méthode « Go To test »
• Choisir « Create New Test »
• Fenêtre de création
• Au besoin déplacer / renommer le dossier
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 09 / 57
• Pour les imports : org.junit…
– Ne pas prendre les autres propositions
• Pour créer les fichiers :
– Soit manuellement
– Soit avec l’ide :
• Clic droit sur une méthode « Go To test »
• Choisir « Create New Test »
• Fenêtre de création
• Au besoin déplacer / renommer le dossier
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 10 / 57
• à préciser en début de classe de test : @RunWith(MockitoJUnitRunner.cla
ss)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 11 / 57
@Test
public void monTest() throws Exception {
ClassLoader classLoader = this.getClass().getClassLoader();
URL resoclassLoader.getResource("storage_input_test.txt");
File data = new File(resource.getPath());
FileInputStream fis = new FileInputStream(data);
// etc.
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 12 / 57
Classes ayant besoin de l’environnement Android pour être créées ou pour êtres utilisés
(Build Variants : Android Instrumentation Tests pour android studio < 2)
Configuration du build.graddle
Besoin d’une Activity => création automatique
Mock et Activity => création plus contrôlée
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 13 / 57
• Pour lancer les tests en Junit4
• Pour séparer les ressources pour les tests de celles de l’application
• Intégration de Junit et mockito
– Mockito 2 pas encore stable
• Les règles pour lancer junit 4
• Pour espresso et uiautomator
• Pour Mockito (génération de dex et non pas de class)
android {
// [...]
defaultConfig {
// [...]
// pour que les tests se lancent en JUnit4
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
sourceSets {
androidTest {
main.res.srcDirs = ['src/main/res', 'src/androidTest/res']
}
}
}
dependencies {
// [...]
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'org.mockito:mockito-core:1.+‘
androidTestCompile 'com.android.support.test:runner:0.4.1
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support:support-annotations:23.1.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.+'
androidTestCompile 'com.crittercism.dexmaker:dexmaker-dx:1.+'
androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.+'
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 14 / 57
// annotation permettant de lancer le test (Junit 4)
@RunWith(AndroidJUnit4.class)
public class ConversionTest {
// lancement de l’activité : initialisation dès le début du test
// mActivityRule permet d’accéder à l’activité = le contexte
// sinon InstrumentationRegistry.getInstrumentation() permet
// d’accéder à l’intrumentation (environnement android)
@Rule
public ActivityTestRule<TodoList> mActivityRule =
new ActivityTestRule<>(TodoList.class);
@Before
public void startTest() { }
@After
public void endTest() { }
@Test
public void monTest() { }
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 15 / 57
• Besoin d’initialisation– @RunWith(MockitoJUnitRunner.class) lance JUnit3...– Initialisation dans le @Before
• Ensemble d’instruction à lancer :// lance l’activity TodoList pour les tests
@RuleActivityTestRule<TodoList> mActivityRule = new ActivityTestRule<>(TodoList.class);
Context initialContext;
@Beforepublic void startTest() throws Exception {
// [...]initialContext = mActivityRule.getActivity();System.setProperty("dexmaker.dexcache", initialContext.getCacheDir().getPath());
MockitoAnnotations.initMocks(this);}
• Pb : initialContext suppose que l’application soit « lancée »... s’il faut faire un mock pour l’initialisation…
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 16 / 57
• Une application se lance avec les données sauvegardées d’une session à une autre
• Mais pour les tests, cela pose problème : il n’y a pas de contrôle des sessions passées
• Besoin de « mock » pour l’initialisation– Ne pas charger pour les tests
• Solution – paramétrer le chargement dans l’Activity à un Intent
– Faire un chargement « paresseux » (lazzy) dans le test
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 17 / 57
@Rule
public ActivityTestRule<TodoList> mActivityRule = new ActivityTestRule<>(TodoList.class, true, false);
@Mock
Sauvegarde mockSauvegarde;
// pour des raisons d’initialisation, à déclarer en Context et non pas en TodoList
// (TodoList est la classe réelle)
Context todo;
@Before
public void startTest() throws Exception {
// Lazily start the Activity from the ActivityTestRule this time to inject the start Intent
Intent startIntent = new Intent();
startIntent.putExtra(TodoList.MUSTLOAD, false);
mActivityRule.launchActivity(startIntent);
todo = mActivityRule.getActivity();
System.setProperty("dexmaker.dexcache", todo.getCacheDir().getPath());
MockitoAnnotations.initMocks(this);
// [...] création du mock
// [...] et affectation...
when(mockSauvegarde.charger(any(Context.class))).thenReturn(loaded);
TodoList.sauvegarde = mockSauvegarde;
// après on utilise le mock
((TodoList) todo).chargerNoteEnregistees();
// et on attend que l’activity ne soit plus active
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 18 / 57
// en cas de modification « static », il faut remettre en
// « place » pour les tests suivants
@After
public void endTest() {
// on remet la valeur par défaut
TodoList.sauvegarde = Sauvegarde.getInstance();
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 19 / 57
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_ui);
boolean loadingToDo = true;
loadingToDo = getIntent().getBooleanExtra(MUSTLOAD, true); // si Intent absent, true par défaut
// [...]
/** loading **/
/** but not in a rotation... or a restore... **/
if ((savedInstanceState == null) && loadingToDo) {
/*** code initial, non testable separement... ***/
/*
si le code de chargerNoteEnregistees() est ici alors ce n'est pas testable sans lancer
toute l'application...
*/
chargerNoteEnregistees();
}
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 20 / 57
• On peut accéder à l’activité par : mActivityRule.getActivity();
• On peut accéder à l’environnement Android par : InstrumentationRegistry.getInstru
mentation()
– Cela permet notamment : InstrumentationRegistry.getInstrumen
tation().waitForIdleSync();
– attente actions “graphique” terminées
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 21 / 57
@Test
public void testCreationItem() {
// le chargement des données, par un mock, est fait préalablement
// un cast est nécessaire
List<Item> items = ((TodoList) mActivityRule.getActivity()).getListItem();
// notes est un tableau, il fait partie de l’oracle
// Item est une classe de l’application
// il est ici vérifier que les éléments ont été créés correctement
assertEquals("nb item attendu : "+notes.length, notes.length, items.size());
for(int i = 0; i < notes.length; i++) {
Item item = items.get(i);
assertEquals("texte de "+i, notes[i], item.getNote());
assertEquals("creation de "+i, (long) buttoirs[i], item.getButtoir());
assertEquals("buttoir de "+i, (long) creations[i], item.getCreation());
}
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 22 / 57
Impact sur l’architecture : isoler ce qui doit être testé unitairement
Conséquences : modularité, contraintes sur l’architecture
Illustration avec la sauvegarde locale
version instrumentée
version locale
Permet l’utilisation à la librairie Mockito
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 23 / 57
• 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/]
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 24 / 57
• 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
Activity
onCreate
Load
Activity
onCreate
load()
invokes
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 25 / 57
• 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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 25 / 57
• 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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 26 / 57
• 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
• 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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 27 / 57
• Appel à setContextMyMockContext context = new MyMockContext(R.raw.storage_input_test);
• 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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 28 / 57
• 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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 29 / 57
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 « written », champ de
// classe de MyMockContext
// mock write(int)
// mock write(byte[])
// mock write(byte[], int, int)
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 30 / 57
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
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 31 / 57
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());
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 32 / 57
• 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);
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 33 / 57
• Séparer
– L’obtention des I/O Stream
– De leur utilisation
TodoList(Activity)
SauvegardeDonnees[] charger(Context c)
void sauvegarder(List<Item> mesItems, Context c)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 33 / 57
• Séparer
– L’obtention des I/O Stream
– De leur utilisation
TodoList(Activity)
SauvegardeEncapsuleeDonnees[] charger(Context c)
void sauvegarder(List<Item> mesItems, Context c)
EntreeSortieFichierArrayList<Donnees> chargerDepuisStream(FileInputStream fis, String
SEPARATOR)
void enregistrerVersStream(List<Item> mesItems, FileOutputStream
fos, String SEPARATOR)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 34 / 57
• EntreeSortieFichier– Indépendance d’Android
• Réutilisable
• Tests locaux
– Paramétrage à choisir
• Transparent pour TodoList
• SauvegardeEncapsulee– Modèle pour l’utilisation de capteur : séparation
de l’acquisition du traitement
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 35 / 57
Cas particulier de tests instrumentés
Utilisation de l’extérieur (à la place d’un « utilisateur »)
http://developer.android.com/tools/testing-support-
library/index.html#Espresso
http://developer.android.com/tools/testing-support-
library/index.html#UIAutomator
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 36 / 57
• Lancement avec ActivityTestRule– « Comme » un test instrumenté
• Permet de faire des actions– Sans s’occuper du thread graphique
• Permet de faire des vérifications
• Fonctionne avec des Matchers / Actions / Assertions
• https://developer.android.com/training/testing/ui-testing/espresso-testing.html
• https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 37 / 57
…(Intent)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 38 / 57
onView(withId(R.id.ajouter)).perform(click());
android.support.test.espresso.Espresso.onView
android.support.test.espresso.matcher.ViewMatchers.withIdUn matcher pour désigner un éléments avec son id… doit être sur l’écran
android.support.test.espresso.action.ViewActions.click
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 39 / 57
public static Matcher<View> numberOfChildren (final int size) {
return new TypeSafeMatcher<View>() {
int mysize = 0;
@Override public boolean matchesSafely (final View view) {
boolean result = false;
if (view instanceof ViewGroup) {
mysize = ((ViewGroup) view).getChildCount ();
result = (mysize == size);
}
return result ;
}
@Override public void describeTo (final Description description) {
description.appendText ("ViewGroup should have " + size + " items and has "+mysize);
}
};
}
Pour une utilisation :
onView(withId(R.id.liste)).check(ViewAssertions.matches(numberOfChildren(i + 1)));
Test effectué : si renvoie « true », cela correspond
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 40 / 57
• Si l’écran est trop petit…
– Les sélections des objets graphiques ne sont possibles que si les objets sont visibles…
– … erreur dans les
• Si le layout dépend de la résolution…
• …
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 41 / 57
• Permet de « sortir » de l’application– Accès au dispositif, exemple : mDevice.setOrientationLeft();
– Sélection des objets graphique avec UI Automatorou Espresso
• Lancement de l’application particulier (c.f. transparent suivant)
• https://developer.android.com/training/testing/ui-testing/uiautomator-testing.html
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 42 / 57
// dans le @Before
// UiDevice mDevice;
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mDevice.waitForIdle();
// Wait for launcher
final String launcherPackage = mDevice.getLauncherPackageName();
assertThat(launcherPackage, notNullValue());
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), 5000);
// Launch the app
Context context = InstrumentationRegistry.getContext();
final Intent intent = context.getPackageManager().
getLaunchIntentForPackage("gonin.renevier.philippe.exempletest");
intent.putExtra(TodoList.MUSTLOAD, false);
// Clear out any previous instances
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
// attente fin creation
mDevice.waitForWindowUpdate("gonin.renevier.philippe.exempletest",1000);
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 43 / 57
• 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)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 44 / 57
Retour sur Espresso, comment faire son ViewAction, comment
enregistrer et rejouer pour des tests
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 45 / 57
• Reprise de DemoMulti(touch)
– En ne considérant qu’un seul touch
• Tester si le tracé par l’utilisateur est bien interprété
– Il faut pouvoir dessiner pour vérifier
– Dessiner de manière automatique
• Pour savoir le résultat attentu
• Pour automatiser les tests
• Bref, pour faire des tests…
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 46 / 57
• Il faut pouvoir « simuler » un touch
• Format du test graphique : onView(<un matcher>).perform(<une action>)
• Mais… parmi les ViewAction… il n’y a pas de touch…
• Il faut donc faire sa propre ViewAction
• Ne pas oublier de modifier le build.graddle
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 47 / 57
• http://developer.android.com/reference/android/support/test/espresso/ViewAction.html
• 3 méthodes : – abstract Matcher<View> getConstraints()
• A mechanism for ViewActions to specify what type of views they can operate on.
– abstract String getDescription()• Returns a description of the view action.
– abstract void perform(UiController uiController, View view)• Performs this action on the given view.
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 48 / 57
public static ViewAction singleTouchAt(final int x, final int y, final int action) {
return new ViewAction() {@Overridepublic void perform(UiController uiController, View view) {
long date = SystemClock.uptimeMillis();MotionEvent me = MotionEvent.obtain(date, date, action, x, y,
0);view.onTouchEvent(me);
}
@Overridepublic String getDescription() {
return "touch ";}
@Overridepublic Matcher<View> getConstraints() {
return ViewMatchers.isAssignableFrom(View.class);}
};}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 49 / 57
• ViewMatchers.isAssignableFrom appelera la méthode isAssignableFrom de Class
• Permet de savoir si la View sera du bon type
• Ici, toutes les views ok
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 50 / 57
• Pour réaliser l’action
• Ici, on appelle onTouchEvent
– Création d’un MotionEvent
• Par une Fabrique (méthode static)
• Action : pour indiquer quel genre de touch (down, move, up, etc.)
– Appel à la méthode
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 51 / 57
• Sur le nombre de points
• Sur les coordonnées des points
• On peut déceler des erreurs…
– Par exemple, oubli du premier et du dernier point
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 52 / 57
• A priori… il faut entrer les coordonnées à la main… – Fastidieux
– Est-on sûr
• donc… non
• Mais comment fait-on pour essayer ? – On « utilise »
• L’idée est donc d’enregistrer ces utilisations, sachant le résultat « attendu » (voire idéal)
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 53 / 57
/storage/sdcard0/Android/data/fr.unice.reneviergonin.demomulti/files
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 54 / 57
• Accès public, même par les autres applications. – Facile d’accès. – On enregistre dedans et on ré-utilise
• Pour ouvrir un fichier : new File(getExternalFilesDir(null), "nom du fichier");– getExternalFilesDir une méthode de ContextWrapper (donc Acticity)– Le fichier « nom du fichier » est dans le dossier files d’un dossier au
nom de package de l’application dans le dossier data du dossier Android sur la sdcard
• Ensuite, ce sont les flux java…• L’enregistrement fait ici dans la même application… mais devrait
être dans un autre projet / code
– Astuce : utilisation de MediaScannerConnection.scanFilepour que les fichiers soient visibles par l’ordinateur
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 55 / 57
File fileLog = new File(getExternalFilesDir(null), format.format(now)+".csv");
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new
FileWriter(fileLog, true)));
for(MotionEvent e : list) {
String line = "" + e.getDownTime() + ";"+ e.getEventTime()+ ";"+
e.getAction()+";"+e.getX()+";"+e.getY()+";"+e.getMetaState();
out.println(line);
}
out.flush();
out.close();
scanFile(fileLog.getPath());
} catch (IOException e) {
e.printStackTrace();
}
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 56 / 57
• Dans le build.graddle : ne pas compresser• Les ressources pour les tests• Chargement via un AssetFileDescriptor
AssetFileDescriptor afd = mActivityRule.getActivity(). getResources().openRawResourceFd(id);
InputStream is = afd.createInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique
Pro
jet de Licen
ce –2
01
5-2
01
6
Philippe Renevier Gonin - L3 Informatique - Projet de Licence, Semestre 6 - Exemple de test avec Espresso 57 / 57
• Chargement du fichier raw
• Création des MotionEvent à partir de cela
• On peut les refaire – Besoin d’un ViewAction avec plus de paramètre
• À partir de là on peut faire des tests…
• … Par exemple pour détecter une ligne horizontale– Voir l’effet de seuil
Tests & Android Tests Locaux Tests InstrumentésTests et Architecture
Tests Graphiquesexemple de test graphique