111
Moles, Stubs et Pex Test Unitaires Isolés et Paramétrisés Jonathan “Peli” de Halleux Microsoft Research

Stubs And Moles Dot Net Hub

Embed Size (px)

Citation preview

Page 1: Stubs And Moles Dot Net Hub

Moles, Stubs et PexTest Unitaires Isolés et Paramétrisés

Jonathan “Peli” de HalleuxMicrosoft Research

Page 2: Stubs And Moles Dot Net Hub

Objectifs de cette sessionA la fin de cette session, je saurai

Ecrire des tests unitaires

Utiliser Moles et Stubs pour isoler les tests

Ecrire des test unitaires paramétrisé avec Pex

Ecrire des tests basés sur des transitions d’états

Page 3: Stubs And Moles Dot Net Hub

Test Unitaire

Page 4: Stubs And Moles Dot Net Hub

A test unitaire est un programme contenant des assertions qui teste une unité de code

Test Unitaire

void PushNonEmpty() { var stack = new Stack(); stack.Push(3); Assert.IsFalse(stack.Empty);}

Page 5: Stubs And Moles Dot Net Hub

Quiz Les thèmes des questions du quiz…

Couverture de code Assertions Isolation La définition de tests unitaires

Page 6: Stubs And Moles Dot Net Hub

Le Code à Tester

string ReadFooValue() { string[] lines = File.ReadAllLines(@"t:\myapp.ini"); foreach (var line in lines) { int index = line.IndexOf('='); string name = line.Substring(index); if (name == "Foo") { string value = line.Substring(index); return value; } } return null;}

Page 7: Stubs And Moles Dot Net Hub

Quiz: Couverture

Combien de couverture de blocs a-t-on besoin?1. 50%2. 80%3. 100%4. La couverture de blocs n’est pas

suffisante

Page 8: Stubs And Moles Dot Net Hub

Quiz: Couverture

Combien de couverture de blocs a-t-on besoin?1. 50%2. 80%3. 100%4. La couverture de blocs n’est pas

suffisante

Page 9: Stubs And Moles Dot Net Hub

Quiz: Couverture

Combien de tests pour 100% de couverture de blocs?1. 12. 23. 34. 105. 1000

Page 10: Stubs And Moles Dot Net Hub

Quiz: Couverture

Combien de tests pour 100% de couverture de blocs?1. 12. 2 3. 34. 105. 1000

Page 11: Stubs And Moles Dot Net Hub

Quiz: Couverture

Quel autre test pour 100% cov.?

[TestMethod]void ExistingFoo() { File.WriteAllLines(@"t:\myapp.ini",new string[]{“Foo=b”}); Reader.ReadFooValue();}

Page 12: Stubs And Moles Dot Net Hub

Quiz: Couverture

Quel autre test pour 100% cov.?

[TestMethod]void MissingFoo() { File.WriteAllLines(@"t:\myapp.ini",new string[]{“a=b”}); Reader.ReadFooValue();}

[TestMethod]void ExistingFoo() { File.WriteAllLines(@"t:\myapp.ini",new string[]{“Foo=b”}); Reader.ReadFooValue();}

Page 13: Stubs And Moles Dot Net Hub

Quiz: Assertions

Pourquoi écrire des Assertions?1. Documentation2. S’assurer que le code est correct3. Faire plaisir au chef4. Prévenir de futurs fautes5. Validater des entrées utilisateur6. Capturer les fautes tôt dans l’exécution

Page 14: Stubs And Moles Dot Net Hub

Quiz: Assertions

Pourquoi écrire des Assertions?1. Documentation2. S’assurer que le code est correct3. Faire plaisir au chef4. Prévenir de futurs fautes5. Validater des entrées utilisateur6. Capturer les fautes tôt dans

l’exécution

Page 15: Stubs And Moles Dot Net Hub

Quiz: Assertions

Quels sont de bons exemples d’assertions?1. Debug.Assert(true);2. Debug.Assert(value != null);3. Debug.Assert(value.Length == line.Length –

(index + 1));4. No assertions

if (name == "Foo") { var value = line.Substring(index); Debug.Assert(???); return value;}

Page 16: Stubs And Moles Dot Net Hub

Quiz: Assertions

Quels sont de bons exemples d’assertions?1. Debug.Assert(true);2. Debug.Assert(value != null);3. Debug.Assert(value.Length == line.Length

– (index + 1));4. No assertions

if (name == "Foo") { var value = line.Substring(index); Debug.Assert(???); return value;}

Page 17: Stubs And Moles Dot Net Hub

Quiz: Assertions

Quels sont de bons exemples d’assertions?1. Assert.IsTrue(value == “b”);2. Assert.IsTrue(value == null);3. Assert.IsTrue(String.IsNullOrEmpty(value))4. Assert.IsTrue(false);5. No assertions

[TestMethod]void MissingFoo() { File.WriteAllLines(@"t:\myapp.ini",new string[]{“a=b”}); string value = Reader.ReadFooValue(); Assert.IsTrue(????);}

Page 18: Stubs And Moles Dot Net Hub

Quiz: Assertions

Quels sont de bons exemples d’assertions?1. Assert.IsTrue(value == “b”);2. Assert.IsTrue(value == null);3. Assert.IsTrue(String.IsNullOrEmpty(value))4. Assert.IsTrue(false);5. No assertions

[TestMethod]void MissingFoo() { File.WriteAllLines(@"t:\myapp.ini",new string[]{“a=b”}); string value = Reader.ReadFooValue(); Assert.IsTrue(????);}

Page 19: Stubs And Moles Dot Net Hub

Quiz: Couverture + Assertions

Comment avoir confiance dans le code?1. Haute couverture, peux d’assertions2. Basse couverture, beaucoup

d’assertions3. Haute couverture, peux d’assertions4. Basse couverture, beaucoup

d’assertions5. “Je l’ai écrit”

Page 20: Stubs And Moles Dot Net Hub

Quiz: Couverture + Assertions

Comment avoir confiance dans le code?1. Haute couverture, peux d’assertions2. Basse couverture, beaucoup

d’assertions3. Haute couverture, beaucoup

d’assertions4. Basse couverture, peux d’assertions5. “Je l’ai écrit”

Page 21: Stubs And Moles Dot Net Hub

Quiz: Couverture + Assertions

Considérons une exécution de tests automatisée

Comment iplementer une assertion,e.g. Debug.Assert?1. { /* ignore */ }2. Log(c);3. if(!c) MessageBox.Show(“Assert!”);4. if(!c) throw new AssertionException();5. Kill process

Page 22: Stubs And Moles Dot Net Hub

Quiz: Couverture + Assertions

Considérons une exécution de tests automatisée

Comment iplementer une assertion,e.g. Debug.Assert?1. { /* ignore */ }2. Log(c);3. if(!c) MessageBox.Show(“Assert!”);4. if(!c) throw new AssertionException();5. Kill process

Page 23: Stubs And Moles Dot Net Hub

Quiz: Couverture + Assertions

Considérons une exécution de tests automatisée

Comment iplementer une assertion,e.g. Debug.Assert?1. { /* ignore */ }2. Log(c);3. if(!c) MessageBox.Show(“Assert!”);4. if(!c) throw new AssertionException();5. Kill process1 assertion =

1 branch

Page 24: Stubs And Moles Dot Net Hub

The Code à Tester

string ReadFooValue() { string[] lines = File.ReadAllLines(@"t:\myapp.ini"); foreach (var line in lines) { int index = line.IndexOf('='); string name = line.Substring(index); if (name == "Foo") { string value = line.Substring(index); return value; } } return null;}

Page 25: Stubs And Moles Dot Net Hub

Quiz: Isolation

Quelles sont les dépendances externes?1. Réseau2. Disque local3. Température ambiante

string ReadFooValue() { string[] lines = File.ReadAllLines(@"t:\myapp.ini"); ...}

Page 26: Stubs And Moles Dot Net Hub

Quiz: Isolation

Quelles sont les dépendances externes?1. Réseau2. Disque dur3. Température ambiante

string ReadFooValue() { string[] lines = File.ReadAllLines(@"t:\myapp.ini"); ...}

Page 27: Stubs And Moles Dot Net Hub

Quiz: Isolation

Quel est le problème du disque dur?1. Le Mapping existe déjà2. Les tests ne peuvent être exécuté en

parralèle3. Le disque est plein4. Droit d’accès5. Pas de problèmes

Page 28: Stubs And Moles Dot Net Hub

Quiz: Isolation

Quel est le problème du disque dur?1. Le Mapping existe déjà2. Les tests ne peuvent être exécuté

en parralèle3. Le disque est plein4. Droit d’accès5. Pas de problèmes

Page 29: Stubs And Moles Dot Net Hub

Quiz: Isolation

Comment gérer le problème?1. Toujours exécuter dans le même

environment2. Refactoring: utiliser des Streams3. Refactoring: introduire IFileSystem4. Refactoring: passer lines comme

argument5. Change l’implémentation de

File.ReadAllLines

Page 30: Stubs And Moles Dot Net Hub

Quiz: Isolation

Comment gérer le problème?1. Toujours exécuter dans le même

environment2. Refactoring: utiliser des Streams3. Refactoring: introduire IFileSystem4. Refactoring: passer lines comme

argument5. Change l’implémentation de

File.ReadAllLines

Abstraction devrait être la

première option

Page 31: Stubs And Moles Dot Net Hub

Quiz: Isolation

Comment gérer le problème?1. Toujours exécuter dans le même

environment2. Refactoring: utiliser des Streams3. Refactoring: introduire IFileSystem4. Refactoring: passer lines comme

argument5. Change l’implémentation de

File.ReadAllLines

Dernier ressort

Page 32: Stubs And Moles Dot Net Hub

Quiz: Isolation

Comment changer File.ReadAllLines?1. Surcharger la procédure2. Recompiler le CLR3. Instrumentation du code4. Demander poliment

Page 33: Stubs And Moles Dot Net Hub

Quiz: Isolation

Comment changer File.ReadAllLines?1. Surcharger la procédure2. Recompiler le CLR3. Instrumentation du code4. Demander poliment

Page 34: Stubs And Moles Dot Net Hub

Quiz: Test Unitaire

Propriétés de test unitaires?1. Connection au réseau2. Exécute longtemps3. En mémoire4. Lit le disque dur5. Exécute rapidement6. Reproducible7. Exécute 10 mèthodes8. Exécute 100 mèthodes9. Exécute 10000 mèthodes10.Requiert une base de données SQL

Page 35: Stubs And Moles Dot Net Hub

Quiz: Test Unitaire

Propriétés de test unitaires?1. Connection au réseau2. Exécute longtemps3. En mémoire4. Lit le disque dur5. Exécute rapidement6. Reproducible7. Exécute 10 mèthodes8. Exécute 100 mèthodes9. Exécute 10000 mèthodes10.Requiert une base de données SQL

Page 36: Stubs And Moles Dot Net Hub

Quiz: La Définition d’un Test Unitaire

Qu’est-ce qu’un Test Unitaire?

Un test unitaire est un programme qui exécute (lentement/rapidement) le code à tester, (avec/sans) dépendences à l’environement, (avec/sans) assertions.

Qu’est-ce qu’une batterie de tests unitaires?

Un ensemble de test unitaires qui atteint un (haut/bas) taux de couverture de code

Page 37: Stubs And Moles Dot Net Hub

Quiz: La Définition d’un Test Unitaire

Qu’est-ce qu’un Test Unitaire?

Un test unitaire est un programme qui exécute rapidement le code à tester, sans dépendences à l’environement, avec assertions.

Qu’est-ce qu’une batterie de tests unitaires?

Un ensemble de test unitaires qui atteint un haut taux de couverture de code

Page 38: Stubs And Moles Dot Net Hub

Isolation avec Moles

Page 39: Stubs And Moles Dot Net Hub

Le code à tester ne devrait pas dépendre de l’environement:

Comment changer File.ReadAllLines?1. Surcharger une méthode statique2. Changer le CLR (et le recompiler)3. Ecrire l’application en javascript4. Instrumentation de code

Le casse tête du testeur

var lines = File.ReadAllLines(@"t:\myapp.ini");

Page 40: Stubs And Moles Dot Net Hub

Le code à tester ne devrait pas dépendre de l’environement:

Comment changer File.ReadAllLines?1. Surcharger une méthode statique2. Changer le CLR (et le recompiler)3. Ecrire l’application en javascript4. Instrumentation de code

Le casse tête du testeur

var lines = File.ReadAllLines(@"t:\myapp.ini");

Page 41: Stubs And Moles Dot Net Hub

Et si on savait changer File.ReadAllLines facilement dans un contexte de test?

Faisons un voeux

File.ReadAllLines = delegate(string fileName) { return new string[]{“a=b”}; }

var lines = File.ReadAllLines(@"t:\myapp.ini");

Page 42: Stubs And Moles Dot Net Hub

Avec Moles, on peux le faire:rediriger toutes les futures exécutions de ReadAllLines vers un délégué qui renvoie {“a=b”}:

Moler des méthodes

var lines = File.ReadAllLines(@"t:\myapp.ini");

MFile.ReadAllLinesString = delegate(string fileName) { return new string[]{“a=b”}; }

Page 43: Stubs And Moles Dot Net Hub

Avec Moles, on peux le faire:rediriger toutes les futures exécutions de ReadAllLines vers un délégué qui renvoie {“a=b”}:

Moler des méthodes

var lines = File.ReadAllLines(@"t:\myapp.ini");

MFile.ReadAllLinesString = delegate(string fileName) { return new string[]{“a=b”}; }

string[] ReadAllLines(string fileName);

Func<string,string[]> ReadAllLines { set; }

Page 44: Stubs And Moles Dot Net Hub

Quiz: Func<T>

Associez les délégués et méthodes

1. Func<string>2. Action3. Action<string>4. Func<bool,string>5. Func<string, bool>6. Action<int>7. Action<List<T>, int>

a) bool File.Exists(string)b) Console.WriteLine(string)c) void Flush()d) String.Empty {get;}e) List<T>.Capacity {set;}

Page 45: Stubs And Moles Dot Net Hub

Quiz: Func<T>

Associez les délégués et méthodes

1. Func<string>2. Action3. Action<string>4. Func<bool,string>5. Func<string, bool>6. Action<int>

a) bool File.Exists(string)b) Console.WriteLine(string)c) void Flush()d) String.Empty {get;}e) List<T>.Capacity {set;}

Page 46: Stubs And Moles Dot Net Hub

C# 3.0 Lambdas

MFile.ReadAllLinesString = delegate(string fileName) { return new string[]{“a=b”}; }

Page 47: Stubs And Moles Dot Net Hub

C# 3.0 Lambdas

MFile.ReadAllLinesString = (fileName) => { return new string[]{“a=b”}; }

Page 48: Stubs And Moles Dot Net Hub

C# 3.0 Lambdas

MFile.ReadAllLinesString = (fileName) => new string[]{“a=b”};

Page 49: Stubs And Moles Dot Net Hub

C# 3.0 Lambdas

MFile.ReadAllLinesString = fileName => new string[]{“a=b”};

Page 50: Stubs And Moles Dot Net Hub

Quiz: Lambdas

Associez les lambdas et méthodes

1. () => “”2. () => {}3. s => {}4. (s) => “”5. (s) => false

a) bool File.Exists(string)b) Console.WriteLine(string)c) void Flush();d) String.Empty {get;}e) string ToString();

Page 51: Stubs And Moles Dot Net Hub

Quiz: Lambdas

Associez les lambdas et méthodes

1. () => “”2. () => {}3. s => {}4. (s) => “”5. (s) => false

a) bool File.Exists(string)b) Console.WriteLine(string)c) void Flush();d) String.Empty {get;}e) string ToString();

Page 52: Stubs And Moles Dot Net Hub

Pour chaque classe une classe mole Une propriété par méthode Conventions de noms

Structure d’un Mole

class File { static string[] FileReadAllLines(string f);}class MFile { Func<string, string[]> FileReadAllLinesString { set; }}

Page 53: Stubs And Moles Dot Net Hub

Quiz: Moles

Convention de noms: ‘M’ + nom de classe Nom de méthode + noms des classes des

paramètres Accesseur de propriétés: Get

Quel est le mole de bool File.Exists(string)?

1. SFile.ExistsString2. MFile.Exists3. MFile.ExistsStringBool4. MFile.ExistsString

Page 54: Stubs And Moles Dot Net Hub

Quiz: Moles

Convention de noms: ‘M’ + nom de classe Nom de méthode + noms des classes des

paramètres Accesseur de propriétés: Get

Quel est le mole de bool File.Exists(string)?

1. SFile.ExistsString2. MFile.Exists3. MFile.ExistsStringBool4. MFile.ExistsString

Page 55: Stubs And Moles Dot Net Hub

Injection de détours dans le corps des méthodes avec un profiler

DétoursCourt circuit de méthodes

Page 56: Stubs And Moles Dot Net Hub

Injection de détours dans le corps des méthodes avec un profiler

DétoursCourt circuit de méthodes

struct DateTime { static DateTime Now {

return InternalNow(); }}

Page 57: Stubs And Moles Dot Net Hub

Injection de détours dans le corps des méthodes avec un profiler

DétoursCourt circuit de méthodes

struct DateTime { static DateTime Now {

return InternalNow(); }}

Func<DateTime> f = GetDetour(...); if(f != null) return f();

Page 58: Stubs And Moles Dot Net Hub

Demo

Page 59: Stubs And Moles Dot Net Hub

“Remplacer quelconque méthode .NET avec un délégué”

L’instrumentation requiert une configuration spéciale:

[HostType(“Moles”)] avec MSTest

MFile.ReadAllLinesString = (f) => new string[0];

Molesen action

Page 60: Stubs And Moles Dot Net Hub

Jusqu’ici on a appris

La définition de tests unitaires

L’isolation de tests unitaires avec Moles

1. Identifier les dépendences externes2. Replacer chaque API externe avec un

délégué

Page 61: Stubs And Moles Dot Net Hub

Pause10 Minutes

Page 62: Stubs And Moles Dot Net Hub

Isolation avec Stubs

Page 63: Stubs And Moles Dot Net Hub

The Code à Tester

string ReadFooValue() { string[] lines = File.ReadAllLines(@"t:\myapp.ini"); foreach (var line in lines) { int index = line.IndexOf('='); string name = line.Substring(index); if (name == "Foo") { string value = line.Substring(index); return value; } } return null;}

Page 64: Stubs And Moles Dot Net Hub

Dependency Injection

string ReadFooValue(IFileSystem fs) { string[] lines = fs.ReadAllLines(@"t:\myapp.ini"); ...}

interface IFileSystem { string[] ReadAllLines(string fileName);}

Page 65: Stubs And Moles Dot Net Hub

Structure d’un Stub

interface IFileSystem { string[] ReadAllLines(string fileName);}

class SIFileSystem : IFileSystem, IStub { public Func<string,string[]> ReadAllLinesString; string[] IFileSystem.ReadAllLines(string name){ return this.ReadAllLinesString(name); } }

Page 66: Stubs And Moles Dot Net Hub

Stub en action

class SIFileSystem : IFileSystem, IStub { public Func<string,string[]> ReadAllLinesString; string[] IFileSystem.ReadAllLines(string name){ return this.ReadAllLinesString(name); } }var lines = { “foo=a”, “b=c”};var fs = new SIFileSystem { ReadAllLinesString = f => lines};

ReadFooValue(fs);

Page 67: Stubs And Moles Dot Net Hub

Jusqu’ici on a appris

La définition de tests unitaires

L’isolation de tests unitaires avec Moles

L’isolation de tests unitaires avec Stubs

Page 68: Stubs And Moles Dot Net Hub

Stubs vs Moles

Moles pour le code des autres “pas le choix”

Stubs pour votre code Refactoring + interfaces + Stubs

Page 69: Stubs And Moles Dot Net Hub

Test UnitaireParamétrisé

Page 70: Stubs And Moles Dot Net Hub

The recette d’un Test Unitaire

var stack = new Stack(); stack.Push(item);

Assert.IsTrue(stack.IsEmpty);}

3 ingrédients étentiels: Données Séquence de méthodes Assertionsvoid PushIsNotEmpty() { int item = 3;

Page 71: Stubs And Moles Dot Net Hub

Quiz: stack.Push (???)

Quelle donnée utiliser?1. 02. 13. int.MaxValue, int.MinValue4. -15. 10006. Peu importe7. Il faux d’abord lire le code

stack.Push(???);

Page 72: Stubs And Moles Dot Net Hub

Quiz: stack.Push (???)

Quelle donnée utiliser?1. 02. 13. int.MaxValue, int.MinValue4. -15. 10006. Peu importe7. Il faux d’abord lire le code

stack.Push(???);

Page 73: Stubs And Moles Dot Net Hub

Test Unitaire Paramétrisé = Test Unitaire avec Parametres

Separation de concepts Spécification Données pour avoir de la couverture de

code

Test Unitaire Paramétrisévoid PushIsNotEmpty(Stack stack, int item) { stack.Push(item); Assert.IsFalse(stack.IsEmpty);}

Page 74: Stubs And Moles Dot Net Hub

Test Unitaire Paramétrisé = Test Unitaire avec Parametres

Separation de concepts Spécification Données pour avoir de la couverture de

code

Test Unitaire Paramétrisévoid PushIsNotEmpty(Stack stack, int item) { stack.Push(item); Assert.IsFalse(stack.IsEmpty);}

pour tout stack,

pour tout item,

… ajouter un element et le stack

n’est pas vide

Page 75: Stubs And Moles Dot Net Hub

Génération de donnéesavec l’exécution dynamique symbolique

Page 76: Stubs And Moles Dot Net Hub

76

Goal: Soit un programme paramétrisé, générer automaticement un ensemble de données qui, à l’exécution, vont atteindre un maximum de chemins d’exécution

Quelle technique pourrait-on utiliser?

Le challenge de la génération de données

Page 77: Stubs And Moles Dot Net Hub

Constraints to solve

a!=null a!=null &&a.Length>0

a!=null &&a.Length>0 &&a[0]==1234567890

void CoverMe(int[] a){ if (a == null) return; if (a.Length > 0) if (a[0] == 1234567890) throw new Exception("bug");}

Observed constraints

a==nulla!=null &&!(a.Length>0)a!=null &&a.Length>0 &&a[0]!=1234567890

a!=null &&a.Length>0 &&a[0]==1234567890

Data

null

{}

{0}

{123…}a==null

a.Length>0

a[0]==123…T

TF

T

F

F

Exécuter&ObserverRésoudreChoiser le prochain chemin

Done: There is no path left.

Exécution dynamique symboliqueExample

Page 78: Stubs And Moles Dot Net Hub

Constraints to solve

Observed constraints

Data

T

TF

T

F

F

Execute&MonitorSolve

Choose next path

Exécution dynamique symboliqueExample

Quels tests Pex générera-t-il?

void CoverMe2( int[] a, int index) { if (a[index] == 1 + a[index + 1]) throw new Exception("bug");}

Page 79: Stubs And Moles Dot Net Hub

Quiz: Exécution dynamique symbolique

Quels tests Pex générera-t-il?

1. (null, 0), ({0,1}, 0)2. (null, 0), ({0,1}, 0), ({0,1}, 1)3. ({}, 1), ({0,0,0,0}, 3)4. (null, 0), ({}, 0), ({0}, 0), ({0},

1), ({1}, 0), ({0,0},0), ({0,1}, 0)

void CoverMe2(int[] a, int index) { if (a[index] == 1 + a[index + 1]) throw new Exception("bug");}

Page 80: Stubs And Moles Dot Net Hub

Demo

Page 81: Stubs And Moles Dot Net Hub

Jusqu’ici on a appris

La définition de tests unitaires

L’isolation de tests unitaires avec Moles

L’isolation de tests unitaires avec Stubs

Test unitaires paramétrisés avec Pex

Page 82: Stubs And Moles Dot Net Hub

Behaved TypesTests à base de transition d’état

Page 83: Stubs And Moles Dot Net Hub

Behaved file system

interface IFileSystem { string[] ReadAllLines(string fileName);}

class BIFileSystem : SIFileSystem { BehavedDictionary<string, string[]> files; public BIFileSystem() { this.files = new BehavedDictionary<string, string[]>(this,”Files”); this.ReadAllLinesString = fn => { string[] lines; if (!this.files.TryGetValue(fn, out lines)) throw new FileNotFoundException(fn); return (string[])lines.Clone(); }; }}

Page 84: Stubs And Moles Dot Net Hub

Behaved file system

interface IFileSystem { string[] ReadAllLines(string fileName);}

On approxime le systême de fichiers par un dictionnaire

Page 85: Stubs And Moles Dot Net Hub

Behaved file system

interface IFileSystem { string[] ReadAllLines(string fileName);}

class BIFileSystem : SIFileSystem { BehavedDictionary<string, string[]> files; public BIFileSystem() { this.files = new BehavedDictionary<string, string[]>(this,”Files”); this.ReadAllLinesString = fn => { string[] lines; if (!this.files.TryGetValue(fn, out lines)) throw new FileNotFoundException(fn); return (string[])lines.Clone(); }; }}

Undefined state

Page 86: Stubs And Moles Dot Net Hub

Démo

Page 87: Stubs And Moles Dot Net Hub

What you learned so far

The Definition of Test Unitaire

Unit Test Isolation through Moles

Unit Tests Isolation through Stubs

Stated-based Unit Tests with Behaved Types

Page 88: Stubs And Moles Dot Net Hub

Break2 Minutes

Page 89: Stubs And Moles Dot Net Hub

Jusqu’ici on a appris

La définition de tests unitaires

L’isolation de tests unitaires avec Moles

L’isolation de tests unitaires avec Stubs

Test unitaires paramétrisés avec Pex

Test à base de transition d’état avec Behaved Types

Page 90: Stubs And Moles Dot Net Hub

Patterns

Page 91: Stubs And Moles Dot Net Hub

PatternNormalized Roundtrip

For an API f(x), f-1(f(f-1(x)) = f-1(x) for all x

void ParseToString(string x) { var normalized = int.Parse(x); var intermediate = normalized.ToString(); var roundtripped = int.Parse(intermediate);

// assert quiz1. Assert(x == intermediate);2. Assert(intermediate == roundtripped);3. Assert(normalized == roundtripped);4. Assert(x == roundtripped); }

Page 92: Stubs And Moles Dot Net Hub

PatternState Relation

Observe a state change

void ContainedAfterAdd(string value) { var list = new List<string>(); list.Add(value);

// assert quiz1. Assert(value != null);2. Assert(list.Contains(value));3. Assert(list.IndexOf(value) < 0);4. Assert(list[0] == value);}

Page 93: Stubs And Moles Dot Net Hub

PatternSame Observable Behavior

Given two methods f(x) and g(x), and a method b(y) that observes the result or the exception behavior of a method, assert that f(x) and g(x) have same observable behavior under b, i.e. b(f(x)) = b(g(x)) for all x.

public void ConcatsBehaveTheSame( string left, string right) { PexAssert.AreBehaviorsEqual( () => StringFormatter.ConcatV1(left, right), () => StringFormatter.ConcatV2(left, right));}

Page 94: Stubs And Moles Dot Net Hub

PatternAllowed Exception

Allowed exception -> negative test case

[PexAllowedException(typeof(ArgumentException))]void Test(object item) { var foo = new Foo(item) // validates item// generated test (C#)[ExpectedException(typeof(ArgumentException))]void Test01() { Test(null); // argument check}

Page 95: Stubs And Moles Dot Net Hub

PatternReachability

Indicate which portions of a PUT should be reachable.

[PexAssertReachEventually]public void Constructor(object input){ new Foo(input); PexAssert.ReachEventually(); }

Page 96: Stubs And Moles Dot Net Hub

PatternRegression Tests

Generated test asserts any observed value Return value, out parameters, PexGoal

When code evolves, breaking changes in observable will be discovered

int AddTest(int a, int b) { return a + b; }

void AddTest01() { var result = AddTest(0, 0); Assert.AreEqual(0, result);}

Page 97: Stubs And Moles Dot Net Hub

More Patterns

Read patterns paper:patterns.pdf

http://research.microsoft.com/Pex/patterns.pdf

Page 98: Stubs And Moles Dot Net Hub

Limitationsand other Details

Page 99: Stubs And Moles Dot Net Hub

The yellow event bar notifies about important events, including certain limitations

Event Bar

Click on issue kind for more information

Page 100: Stubs And Moles Dot Net Hub

Events View

You should act on these events: Refactor your code, or tell Pex to ignore it in the future, let Pex analyze (“instrument”) more code,

if possible.

Page 101: Stubs And Moles Dot Net Hub

Instrumenting more code If Pex reports that some code was

uninstrumented, you may tell Pex to instrument and analyze it(if possible)

Page 102: Stubs And Moles Dot Net Hub

Instrumenting more code

Code instrumentation on Demand Instrumentation has high performance

overhead Some parts of the code better ignored

Use PexInstrument… attributes

Pex will often suggest and insert those attributes for you

[assembly: PexInstrumentAssembly(“Foo”)]

Page 103: Stubs And Moles Dot Net Hub

Pex understand managed .NET code only Pex does not understand native code.

Problem if branching over values obtained from the environment Pex may not automatically detect all such

cases.

Testability

if (!File.Exists(f)) throw ...

File System?

Page 104: Stubs And Moles Dot Net Hub

Hidden Complexity

Pex analyzes every executed .NET instruction

Some used libraries may be surprisingly expensive to analyze XML parsing repeatedly converting data between different

representationsvoid Sum(string[] A) { var sum = “0”; foreach(var a in A) sum = (int.Parse(a) + int.Parse(sum)).ToString(); if(sum == “123”) throw new Exception(); }

Don’t do this.

Page 105: Stubs And Moles Dot Net Hub

Exploration Boundaries

Configurable bounds include: TimeOut MaxBranches MaxCalls MaxConditions▪ Number of conditions that depend on test

inputs MaxRuns ConstraintSolverTimeOut ConstraintSolverMemoryLimit

Page 106: Stubs And Moles Dot Net Hub

Multi-threaded code

Unlike test inputs, thread-interleavings can normally not be controlled

Thus, Pex can only explore single-threaded code

Related approach to explore thread-schedules (but not input parameters) by controlling thread-scheduler: CHESShttp://research.microsoft.com/CHESS

Page 107: Stubs And Moles Dot Net Hub

Lack of Test Oracle

Write assertions and Pex will try to break them

Without assertions, Pex can only find violations of runtime contracts causing NullReferenceException, IndexOutOfRangeException, etc.

Assertions leveraged in product and test code

Pex can leverage Code Contracts (discussed later)

Page 108: Stubs And Moles Dot Net Hub

Exercise Limitations

Goal: Understand limitations.

Apply Pex to

if (File.Exists(fileName)) throw new Exception(“found it”);

if (DateTime.Parse(s).Year == 2009) throw new Exception(“found it”);

Page 109: Stubs And Moles Dot Net Hub

Wrapping up

Page 110: Stubs And Moles Dot Net Hub

What you learned so far

The Definition of Test Unitaire

Unit Test Isolation through Moles

Unit Tests Isolation through Stubs

Stated-based Unit Tests with Behaved Types

Write Pex parameterized unit tests

Page 111: Stubs And Moles Dot Net Hub

Thank you

http://research.microsoft.com/pex