Test unitaires - refactoring - clean code

56
UNIT TEST Refactoring Clean code Avril 2017

Transcript of Test unitaires - refactoring - clean code

UNIT TESTRefactoringClean code

Avril 2017

I am Hadrien Blanc

Responsable pédagogique Epitech

@hadrienblanc

[email protected]

www.linkedin.com/in/hadrienblanc

HELLO!

Ariane 5 1/2

Ariane 5 cas "funny":▸Explosion après 40

secondes▸10 ans et 7 milliards de $▸Conversion d'un float 64 bit

vers un entier non signé 16 bit

Ariane 5 2/2

Principale explication:"no test was performed to verify that the Inertial Reference System would behave correctly when being subjected to the count-down and flight time sequence and the trajectory of Ariane 5."▸ ARIANE 5 Flight 501 Failure Report by the Inquiry Board

The Chairman of the Board : Prof. J. L. LIONS

“Software must be soft: it has to be easy to change because it will change despite our misguided efforts otherwise.”

The pragmatic Programmers

Cependant, le code est difficile à modifier lorsqu'il est :

1. Pas lisible2. Dupliqué3. Compliqué

"Any fool can write code that a computer can understand"

Martin Fowler

"Good programmer write code that humans can understand"

Martin Fowler

Lisibilité du code

Pistes sur la lisibilité du code:

▸ Style d’Indentation cohérent▸ Éviter les commentaires évidents ▸ Nommage des variables et fonctions

cohérents▸ DRY - Don’t Repeat Yourself▸ Niveaux multiples de parenthèses▸ Taille limite de la ligne▸ Organisation des dossiers ▸ KISS ▸ Utilisation de Design pattern

Style d'indentation cohérent 1/2

while (x == y) {

something();

somethingelse();

}

K&R and variants:

1TBS, Stroustrup, Linux kernel, BSD KNF

while (x == y)

{

something();

somethingelse();

}

Allman style (Bsd)

while (x == y)

{

something();

somethingelse();

}

Gnu

while (x == y)

{

something();

somethingelse();

}

Whitesmiths

Style d'indentation cohérent 2/2

while (x == y)

{ something();

somethingelse();

}

Horstmann style

while (x == y)

{ something();

somethingelse(); }

Pico

while (x == y) {

something();

somethingelse();

}

Ratliff

while (x == y) {

something();

somethingelse(); }

Lisp

Éviter les commentaires évidents 1/2

//Extraire les données de l’ancien système

functionD1();

//Transformer les données

functionD2();

//Charger les données dans le nouveau système

functionD3();

VS

Extract();

Transform();

Load();

"Si le code est tellement compliqué que cela doit

être expliqué, il est presque toujours préférable

d’améliorer le code que d’ajouter des

commentaires"

▸ Steve Mcconnell

Éviter les commentaires évidents 2/2

Exemple

VS

Nommages de variable cohérent 1/2

Les noms des constantes symboliques et des

macros instructions doivent être écrits en

majuscule.

#define LONGUEUR_NOM_FICHIER 15

#define STR(s) #s

#define XSTR(s) STR(s)

Nommages de variable cohérent 2/2

camelCase VS Lowercase

def my_function():

or

def myFunction():

DRY Don't Repeat Yourself 1/2

"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."

Livre The Pragmatic Programmer

VSWET

"Write Everything Twice""We Enjoy Typing""Waste Everyone's Time"

DRY 2/2

Echo "Hello Hadrien, how are you doing ?";Echo "Hello Sergei, how are you doing ?";Echo "Hello you, how are you doing ?";

VS

function greetings($str) { echo "Hello $str, how are you doing ? " }greetings("Hadrien");greetings("Sergei");greetings("you");

Niveau multiple de parenthèses

If (((2 + $x) > 3) || (($y + 2) > 6)) && ($x + $y > 1)) {...}

VS$cond1 = (2 + x) > 3;$cond2 = ((y + 2) > 6);$cond1_ou_2 = $cond1 || $cond2;$cond3 = $x + $y > 1;$cond1_ou_2_et_cond3 =

$cond1_ou_2 && $cond3;If ($cond1_ou_2_et_cond3)

{...}

Organisation des dossiers

app/The application configuration, templates and

translations.bin/

Executable files (e.g. bin/console).src/

The project's PHP code.tests/

Automatic tests (e.g. Unit tests).var/

Generated files (cache, logs, etc.).vendor/

The third-party dependencies.web/

The web root directory.

KISS

KISS - Keep It Simple

1.D'abord un code fonctionnel

2.Puis optimisation

3.En gardant un code

fonctionnel

Design Pattern

▸ Factory MethodCréation d'objet sans spécifier la classe exacte$factory = new SedanFactory();

$car = $factory->makeCar();

print $car->getType();

▸ SingletonUtilisation d'un objet dans le code▸ FacadeSimplifier une interface

S'améliorer

Comment améliorer la lisibilité, simplicité, éviter la duplication du code ?

Pistes: ▸ Parcourir du code open-source

(contribuer ?)▸ Lire des blogs▸ Lire des livres▸ ...

Code metrics

▸Information hiding▸Encapsulation▸Coupling

Loose coupling / tight coupling

▸Dependency▸Cyclomatic complexity

The number of linearly independent paths through a program's source code. (Foret de IF IF IF IF …)

"If you want to refactor, the essential precondition is

having solid TESTS"

Martin Fowler

Unit testLes tests unitaires

WHY ?

Unit test : pourquoi ?Le métier d'ingénieur logiciel évolue :▸ Désign▸ Développement ▸ Test▸ Déploiement ▸ Résolution de bugs

Concepteur/Architecte + Développeur + Ingénieur qualité + administrateur système +

ingénieur production

Code et utilisateur

CODE

Utilisateur

Tests unitaires

Tests fonctionnels

Tests d'acceptations

Objectif

Objectif du test:▸Démonstration que le

programme fonctionne sans erreur▸Avoir le comportement

logiciel voulu▸Augmenter la qualité

logicielle

Test Driven DevelopmentTDD

"Le test-driven development (TDD) ou en français développement piloté par les tests est une technique de développement de logiciel qui préconise d'écrire les tests unitaires avant d'écrire le code source d'un logiciel."

-- Wikipedia

Ne pas confondre

1. Écrire du code

2. Ajout des tests

3. Modification du code pour passer

VS

1. Écrire des tests

2. Écrire du code qui passe les tests

(Test Driven Development)

TDD En détail

Le cycle TDD en détail : 1. Écrire un premier test2. Vérifier qu'il échoue (car le code

n'existe pas encore)3. Écrire juste le code suffisant pour

passer le test4. Vérifier que le test passe5. Puis réusiner le code, refactoring.

Réusiner ?Refactoring ?

Réusiner /

Refactoring (in english)

Changer le code existant sans

changer son comportement

extérieur

Nouvelle fonctionnalité

Scénario #1 : Développer une

nouvelle fonctionnalité

1. Écrire un test pour une nouvelle

fonctionnalité (tests KO)

2. Lancer le test, il échoue (tests KO)

3. Écrire assez de code pour passer le

test (tests OK)

4. Refactoring pour faire le code de

meilleure qualité (tests OK)

Correction de bug

Scénario #2 : Correction de bug

/ erreur

1. Écrire un test du code avec un bug

- documentation (tests KO)

2. Faire les modification, le test

passe (tests OK)

3. Refactoring (tests OK)

Refactoring

Scénario #3 : Refactoring

1.Écrire/modifier les tests

unitaires (tests KO)

2.Écrire le code (tests KO)

3.Refactoring (tests OK)

Legacy code

Scénario #4 : Legacy code

▸Écrire des tests unitaires

pour commencer à

comprendre du vieux code

ou un système déjà en place

Learning

Scénario #5 : Learning

▸Utiliser une nouvelle API

▸Faire un nouvel algorithme

▸Le test est maintenant une

base de savoir réutilisable

Knowledge Management

Découverte d'une API / outils▸Permet d'avoir une base de

connaissance partagé sur la nouvelle technologie : Knowledge Management

Question

Comment faire des tests unitaires maintenables ?

Diagram AAA "Arrange-Act-Assert"

A pattern for arranging and

formatting code :

1.Arrange all necessary

preconditions and inputs.

2.Act on the object or method

under test.

3.Assert that the expected results

have occurred.

Diagram "AAA" 1/3

Diagram "AAA" 2/3

Example:

@Testpublic void test() {

String input = "abc";

String result = Util.reverse(input);

assertEquals("cba", result);}

Diagram "AAA" 3/3

Avantages:

▸ Séparation clair de ce qui est testé : entrée, fonction, résultat

▸ Met la priorité aux paliers nécessaires dans le test du code

▸ Ne pas faire trop de choses différentes à la fois

Unit test - In a team#management

Continuous Integration

Intégration continue

▸Lancer les tests unitaires

lorsqu'il y a un changement

dans le code

Continuous Integration

"In software engineering, continuous integration (CI) is the practice of merging all developer working copies to a shared mainline several times a day.Grady Booch first named and proposed CI in his 1991 method, although he did not advocate integrating several times a day. Extreme programming (XP) adopted the concept of CI and did advocate integrating more than once per day -perhaps as many as tens of times per day."

-- wikipedia

Extreme Programming :▸ Objectif : produire du code de qualité de

manière productive▸ Cycles de développement courts▸ Changer le code n'affecte pas les

fonctionnalités▸ Le code est au centre▸ Unit tests / acceptance tests (client)▸ Communication, simplicity, feedback, and

courage▸ Peer-programming

XP

Nouvel arrivant : unit test

Nouvel arrivant :

▸Permet de se familiariser

avec du code.

▸Documentation et

spécification de comment

utiliser le code

Contrôle de la régression

En cas de gros changement de codebase : outils contre la Régression

Lors de :

▸Refactoring

▸De grands changements

Frameworkde tests

Framework de tests

▸JUnit▸PHPunit▸cUnit▸...

Libcheck 1/2

#include <stdlib.h>

#include <check.h>

#include "../src/money.h"

START_TEST(test_money_create)

{

Money *m;

m = money_create(5, "USD");

ck_assert_int_eq(money_amount(m), 5);

ck_assert_str_eq(money_currency(m), "USD");

money_free(m);

}

END_TEST

More on https://github.com/libcheck/check

Libcheck 2/2

Running suite(s): Money

0%: Checks: 1, Failures: 1, Errors: 0

check_money.c:9:F:Core:test_money_create:0: Assertion

'money_amount (m)==5' failed:

money_amount (m)==0, 5==5

FAIL: check_money

=====================================================

1 of 1 test failed

Please report to check-devel AT lists.sourceforge.net

=====================================================

Ruby on Rails

Rails test

require 'test_helper'

class modelTest < ActiveSupport::TestCasetest "the truth" doassert true

endEnd

bin/rake test test/models/project_test.rb

Run options: --seed 227

# Running:

.

Finished in 0.173677s, 5.7578 runs/s, 5.7578 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

Available assertions

Assertion Purpose

assert( test, [msg] ) Ensures that test is true.

assert_not( test, [msg] ) Ensures that test is false.

assert_equal( expected, actual,

[msg] )

Ensures that expected == actual is

true.

assert_not_equal( expected,

actual, [msg] )

Ensures that expected != actual is

true.

assert_same( expected, actual,

[msg] )

Ensures that

expected.equal?(actual) is true.

assert_not_same( expected,

actual, [msg] )

Ensures that

expected.equal?(actual) is false.

assert_nil( obj, [msg] ) Ensures that obj.nil? is true.

assert_not_nil( obj, [msg] ) Ensures that obj.nil? is false.

assert_empty( obj, [msg] ) Ensures that obj is empty?.

*http://guides.rubyonrails.org/testing.html

Rails

FixturesFixtures are a way of organizing data that you

want to test against; in short, sample data.

rubyonrails:

id: 1

name: Ruby on Rails

url: http://www.rubyonrails.org

google:

id: 2

name: Google

url: http://www.google.com

Qu'en pensez-vous ?Merci de votre attention