Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Mutation testing
Gotta Kill ’Em All !
Loïc Knuchel
Loïc Knuchel@loicknuchel
Développeur Scala chez
Organisateur des Paris
Software craftsman
loicknuchel@gmail.com
DDD
FP
CQR...
Loïc Knuchel - @loicknuchel
● Peu de bugs
● Lisible par un autre
développeur
● Pas trop dur à faire
évoluer
Loïc Knuchel - @loicknuchel
Loïc Knuchel - @loicknuchel
Stratégies de test
Loïc Knuchel - @loicknuchel
Du code
robuste grâce
aux tests
Loïc Knuchel - @loicknuchel
Tester les
tests
Loïc Knuchel - @loicknuchel
Etape 1: l’intuition
Loïc Knuchel - @loicknuchel
Etape 2: couverture de code
Loïc Knuchel - @loicknuchel
Solution 2: couverture de code
Loïc Knuchel - @loicknuchel
Code exécuté par des tests != code testé
class Cart(size: Int) {
val items = mutable.ArrayBuff...
Loïc Knuchel - @loicknuchel
Code exécuté par des tests != code testé
it("has irrelevant assert") {
new Cart(3).add("shoes"...
Loïc Knuchel - @loicknuchel
Code exécuté par des tests != code testé
class Cart(size: Int) {
val items = mutable.ArrayBuff...
Loïc Knuchel - @loicknuchel
Code exécuté par des tests != code testé
Loïc Knuchel - @loicknuchel
Tout le code ne se vaut pas
Loïc Knuchel - @loicknuchel
Etape 3
Mutation testing
Loïc Knuchel - @loicknuchel
Mise en place: Java
// pom.xml
<build>
<plugins>
<plugin>
<groupId>org.pitest</groupId>
<artif...
Loïc Knuchel - @loicknuchel
Mise en place: Scala
// project/plugins.sbt
addSbtPlugin("io.github.sugakandrey" % "sbt-scalam...
Loïc Knuchel - @loicknuchel
Mise en place: JavaScript
$ ./node_modules/.bin/stryker run
// stryker.conf.js
module.exports ...
Loïc Knuchel - @loicknuchel
Et plein
d’autres...
NinjaTurtles pour C#
Mutmut pour Python
mutant pour Ruby
Infection pour P...
Loïc Knuchel - @loicknuchel
Mutation testing
Loïc Knuchel - @loicknuchel
Génère un mutant
Lance les tests
Vérifie le résultat
Recommence
Loïc Knuchel - @loicknuchel
Mutant tué
Si les tests échouent
Le code muté a été détecté
Il est donc correctement testé
Loïc Knuchel - @loicknuchel
Mutant vivant
Si les tests réussissent
Le code muté n’a pas été détecté
Les tests sont donc in...
Loïc Knuchel - @loicknuchel
Qu’est-ce qu’un mutant ?
def add(item: String): Boolean = {
println(s"item add: $item")
val ex...
Loïc Knuchel - @loicknuchel
Mutations: conditions
Modification :
< ⇔ <=
> ⇔ >=
&& ⇔ ||
Constante :
true
false
Inversion :
...
Loïc Knuchel - @loicknuchel
Mutations: opération mathématique
Opérations :
x++ ⇔ x--
+ ⇔ -
* ⇔ /
% ⇔ *
Opérations binaires...
Loïc Knuchel - @loicknuchel
Mutations: constantes
Change une constante :
true ⇔ false
0 ⇔ 1
x ⇔ x + 1
x ⇔ null
Remplace un...
Loïc Knuchel - @loicknuchel
Mutations: supprimer une fonction
println(s"item add: $item")
Loïc Knuchel - @loicknuchel
Mutations
Loïc Knuchel - @loicknuchel
En pratique
● mutants uniquement pour le
code couvert par les tests
● lance uniquement les tes...
Loïc Knuchel - @loicknuchel
Exemple
/**
* Take a list of item prices and calculate the bill :
* - if total is higher than ...
Loïc Knuchel - @loicknuchel
Test 1
@Test
public void getPrice_should_be_normal_price_with_few_and_cheap_items() {
assertEq...
Loïc Knuchel - @loicknuchel
Test 1
Loïc Knuchel - @loicknuchel
Test 1 + 2
@Test
public void getPrice_should_be_get_10pc_discound_on_expensive_items() {
asser...
Loïc Knuchel - @loicknuchel
Test 1 + 2
Loïc Knuchel - @loicknuchel
Test 1 + 2 + 3
@Test
public void getPrice_should_be_get_one_free_item_when_buy_many() {
assert...
Loïc Knuchel - @loicknuchel
Test 1 + 2 + 3
Loïc Knuchel - @loicknuchel
Test 1 + 2 + 3 + 4
@Test
public void getPrice_should_should_test_boundary_conditions() {
// 50...
Loïc Knuchel - @loicknuchel
Test 1 + 2 + 3 + 4
Loïc Knuchel - @loicknuchel
Code source
https://github.com/loicknuchel/mutation-testing-sample
Java / Scala / JavaScript /...
Loïc Knuchel - @loicknuchel
Conclusion
Impossible à
contourner
Long à
exécuter
Couplage
code ⇔
tests
Impossible à
tuer
Tes...
Loïc Knuchel - @loicknuchel
Questions ?
Slides:
http://bit.ly/
rivieradev-2018-mutation-testing
Prochain SlideShare
Chargement dans…5
×

Mutation testing, enfin une bonne mesure de la qualité des tests ?, RivieraDev le 18/05/2018

464 vues

Publié le

On écrit tous des tests (n’est-ce pas ?), mais comment savoir s’ils sont utiles ?

- Par leur nombre ? Faux, beaucoup de tests ne garantissent pas que l’application fonctionne correctement
- Avec une bonne couverture du code ? Encore faux, mieux mais pas suffisant

L’important est d'être confiant sur la capacité des tests à détecter les problèmes (c’est pourquoi en TDD un test doit échouer au début, pour etre sur qu’il teste bien quelque chose). Laissez-moi donc vous présenter le mutation testing ! Cette technique modifie votre code, lance les tests et s’attend à ce qu’ils échouent. Si non, c’est que cette partie est mal testée… Dans ce talk je détaillerai les principes du mutation testing, expliquerai comment l’utiliser sur un projet scala et montrerai les résultats obtenus sur un projet réel.

Publié dans : Logiciels
  • Soyez le premier à commenter

Mutation testing, enfin une bonne mesure de la qualité des tests ?, RivieraDev le 18/05/2018

  1. 1. Mutation testing Gotta Kill ’Em All ! Loïc Knuchel
  2. 2. Loïc Knuchel@loicknuchel Développeur Scala chez Organisateur des Paris Software craftsman loicknuchel@gmail.com DDD FP CQRS Hexagonal architecture Property based testing Event Sourcing Event Storming TDD Living documentation
  3. 3. Loïc Knuchel - @loicknuchel ● Peu de bugs ● Lisible par un autre développeur ● Pas trop dur à faire évoluer
  4. 4. Loïc Knuchel - @loicknuchel
  5. 5. Loïc Knuchel - @loicknuchel Stratégies de test
  6. 6. Loïc Knuchel - @loicknuchel Du code robuste grâce aux tests
  7. 7. Loïc Knuchel - @loicknuchel Tester les tests
  8. 8. Loïc Knuchel - @loicknuchel Etape 1: l’intuition
  9. 9. Loïc Knuchel - @loicknuchel Etape 2: couverture de code
  10. 10. Loïc Knuchel - @loicknuchel Solution 2: couverture de code
  11. 11. Loïc Knuchel - @loicknuchel Code exécuté par des tests != code testé class Cart(size: Int) { val items = mutable.ArrayBuffer[String]() def add(item: String): Boolean = { println(s"item add: $item") val exists = items.contains(item) if(items.length < size) { items.append(item) } exists } } it("has no assert") { new Cart(3).add("shoes") }
  12. 12. Loïc Knuchel - @loicknuchel Code exécuté par des tests != code testé it("has irrelevant assert") { new Cart(3).add("shoes") shouldBe false } class Cart(size: Int) { val items = mutable.ArrayBuffer[String]() def add(item: String): Boolean = { println(s"item add: $item") val exists = items.contains(item) if(items.length < size) { items.append(item) } exists } }
  13. 13. Loïc Knuchel - @loicknuchel Code exécuté par des tests != code testé class Cart(size: Int) { val items = mutable.ArrayBuffer[String]() def add(item: String): Boolean = { println(s"item add: $item") val exists = items.contains(item) if(items.length < size) { items.append(item) } exists } } Non testé : ● les effets de bords ● la condition limite ● l’ajout dans la liste it("asserts few things") { val cart = new Cart(3) cart.add("shoes") cart.items.length shouldBe 1 }
  14. 14. Loïc Knuchel - @loicknuchel Code exécuté par des tests != code testé
  15. 15. Loïc Knuchel - @loicknuchel Tout le code ne se vaut pas
  16. 16. Loïc Knuchel - @loicknuchel Etape 3 Mutation testing
  17. 17. Loïc Knuchel - @loicknuchel Mise en place: Java // pom.xml <build> <plugins> <plugin> <groupId>org.pitest</groupId> <artifactId>pitest-maven</artifactId> <version>1.2.4</version> </plugin> </plugins> </build> $ mvn org.pitest:pitest-maven:mutationCoverage
  18. 18. Loïc Knuchel - @loicknuchel Mise en place: Scala // project/plugins.sbt addSbtPlugin("io.github.sugakandrey" % "sbt-scalamu" % "0.1.1") $ sbt mutationTest Scalamu
  19. 19. Loïc Knuchel - @loicknuchel Mise en place: JavaScript $ ./node_modules/.bin/stryker run // stryker.conf.js module.exports = function(config) { config.set({ testRunner: "jest", mutator: "javascript", transpilers: [], reporter: ["html", "progress"], coverageAnalysis: "all", mutate: ["src/**/*.js"] }); }; $ npm install stryker stryker-api stryker-html-reporter stryker-javascript-mutator stryker-jest-runner --save- dev Stryker
  20. 20. Loïc Knuchel - @loicknuchel Et plein d’autres... NinjaTurtles pour C# Mutmut pour Python mutant pour Ruby Infection pour PHP ...
  21. 21. Loïc Knuchel - @loicknuchel Mutation testing
  22. 22. Loïc Knuchel - @loicknuchel Génère un mutant Lance les tests Vérifie le résultat Recommence
  23. 23. Loïc Knuchel - @loicknuchel Mutant tué Si les tests échouent Le code muté a été détecté Il est donc correctement testé
  24. 24. Loïc Knuchel - @loicknuchel Mutant vivant Si les tests réussissent Le code muté n’a pas été détecté Les tests sont donc insuffisants
  25. 25. Loïc Knuchel - @loicknuchel Qu’est-ce qu’un mutant ? def add(item: String): Boolean = { println(s"item add: $item") val exists = items.contains(item) if (items.length < size) { items.append(item) } exists } Code original def add(item: String): Boolean = { () val exists = items.contains(item) if (items.length < size) { items.append(item) } exists } Supprime un appel de fonction def add(item: String): Boolean = { println(s"item add: $item") val exists = items.contains(item) if (true) { items.append(item) } exists } Condition toujours vraie
  26. 26. Loïc Knuchel - @loicknuchel Mutations: conditions Modification : < ⇔ <= > ⇔ >= && ⇔ || Constante : true false Inversion : == ⇔ != < ⇔ >= > ⇔ <= cond ⇔ !cond if (items.size() < size) { items.add(item); } if (items.size() <= size) { items.add(item); }
  27. 27. Loïc Knuchel - @loicknuchel Mutations: opération mathématique Opérations : x++ ⇔ x-- + ⇔ - * ⇔ / % ⇔ * Opérations binaires : & ⇔ | ^ ⇔ & >> ⇔ << return total - discount; return total + discount;
  28. 28. Loïc Knuchel - @loicknuchel Mutations: constantes Change une constante : true ⇔ false 0 ⇔ 1 x ⇔ x + 1 x ⇔ null Remplace une variable par une constante : x ⇔ true / false x ⇔ 0 / 1 / 2 return exists; if(exists == null) throw new RuntimeException(); else return null;
  29. 29. Loïc Knuchel - @loicknuchel Mutations: supprimer une fonction println(s"item add: $item")
  30. 30. Loïc Knuchel - @loicknuchel Mutations
  31. 31. Loïc Knuchel - @loicknuchel En pratique ● mutants uniquement pour le code couvert par les tests ● lance uniquement les tests qui couvrent le code muté ● fonctionne en mode itératif ● à mettre en priorité pour le code critique ● activer que les mutations intéressantes Brute force
  32. 32. Loïc Knuchel - @loicknuchel Exemple /** * Take a list of item prices and calculate the bill : * - if total is higher than 50, apply 10% overall discount * - if more than 5 items, apply 100% discount on cheapest one * - if many discount apply, use the higher one */ public static Double getPrice(List<Double> prices) { Double total = sum(prices); Double discount = 0.0; if (total >= 50) { discount = total * 0.1; } if (prices.size() >= 5) { Double minPrice = min(prices); if (minPrice > discount) { discount = minPrice; } } return total - discount; }
  33. 33. Loïc Knuchel - @loicknuchel Test 1 @Test public void getPrice_should_be_normal_price_with_few_and_cheap_items() { assertEquals(24, Demo.getPrice(Arrays.asList(4, 7, 1, 12), 0.001); }
  34. 34. Loïc Knuchel - @loicknuchel Test 1
  35. 35. Loïc Knuchel - @loicknuchel Test 1 + 2 @Test public void getPrice_should_be_get_10pc_discound_on_expensive_items() { assertEquals(54, Demo.getPrice(Arrays.asList(10, 20, 30)), 0.001); }
  36. 36. Loïc Knuchel - @loicknuchel Test 1 + 2
  37. 37. Loïc Knuchel - @loicknuchel Test 1 + 2 + 3 @Test public void getPrice_should_be_get_one_free_item_when_buy_many() { assertEquals(22, Demo.getPrice(Arrays.asList(3, 5, 2, 8, 1, 4)), 0.001); }
  38. 38. Loïc Knuchel - @loicknuchel Test 1 + 2 + 3
  39. 39. Loïc Knuchel - @loicknuchel Test 1 + 2 + 3 + 4 @Test public void getPrice_should_should_test_boundary_conditions() { // 50 total value boundary assertEquals(45, Demo.getPrice(Arrays.asList(5, 10, 15, 20)), 0.001); // 5 item boundary assertEquals(43, Demo.getPrice(Arrays.asList(7, 8, 15, 10, 10)), 0.001); }
  40. 40. Loïc Knuchel - @loicknuchel Test 1 + 2 + 3 + 4
  41. 41. Loïc Knuchel - @loicknuchel Code source https://github.com/loicknuchel/mutation-testing-sample Java / Scala / JavaScript / PR acceptées ;)
  42. 42. Loïc Knuchel - @loicknuchel Conclusion Impossible à contourner Long à exécuter Couplage code ⇔ tests Impossible à tuer Tester ses tests Meilleure couverture de code ! Facile à mettre en place
  43. 43. Loïc Knuchel - @loicknuchel Questions ? Slides: http://bit.ly/ rivieradev-2018-mutation-testing

×