1. Why testing ?
A majority of the production failures (77%) can be reproduced by a unit test.
1.1. To deliver the right product
1.2. What works for 1 does not necessary for 100
1.3. Murphy's law
Everything that can possibily go wrong, will go wrong.
1.4. Different OS or different terminals
1.5. To provide the best
2. Mandatory tests example
-
Fork the repository.
-
Run
bundle
to install development dependencies. -
Create a topic branch
-
Add tests for your unimplemented feature or bug fix. (See [writing-and-executing-tests])
-
Run
bundle exec rake
to run the tests. If your tests pass, return to step 4. -
Implement your feature or bug fix.
-
Run
bundle exec rake
to run the tests. If your tests fail, return to step 6. -
Add documentation for your feature or bug fix.
-
If your changes are not 100% documented, go back to step 8.
-
Add, commit, and push your changes.
-
Submit a pull request.
3. Mandatory Doc example
[…] an Eclipse project is providing extensible frameworks and applications accessible via documented APIs.
4. Test kinds
Verification |
Validation |
Are you building it right? |
Are you building the right thing? |
Done by developers |
Done by tester |
First |
second |
5. JUnit etc.
5.1. Testing what ?
exceptions |
|
execution time |
|
Only some environment |
|
Do something before other tests (e.g., base access) |
|
5.2. Assertions
|
Force the test to fail |
|
condition is true |
|
condition is false |
|
values are equals |
|
null object |
|
identical objects (same ref.) |
5.3. Tests Strategies
Let’s consider int add(int,int);
of the myClass
class.
Define the "normal" behavior:
//for normal addition
@Test
public void testAdd1Plus1() {
int x = 1 ; int y = 1;
assertEquals(2, myClass.add(x,y));
}
Add some tests for particular cases:
-
no exception captured in case of overflow
-
null
parameters are handled, e.g.:
//if you are using 0 as default for null, make sure your class works in that case.
@Test
public void testAdd1Plus1() {
int y = 1;
assertEquals(0, myClass.add(null,y));
}
-
It works with negative parameters, etc.
5.4. No ordering of tests!!
JUnit assumes that all test methods can be executed in an arbitrary order.
5.5. No ordering of tests (ctd.)!!
Well-written test code should not assume any order, i.e., tests should not depend on other tests.
5.6. Eclipse
-
For an existing class: Right-click (
Package Explorer
and ). -
Use the
JUnit wizards
( ). -
And then
.
Use of the infinitest plug-in.
5.7. What about graphical interfaces?
Example of the Robot
library:
Robot bot = new Robot();
bot.mouseMove(10,10);
bot.mousePress(InputEvent.BUTTON1_MASK);
//add time between press and release or the input event system may
//not think it is a click
try{Thread.sleep(250);}catch(InterruptedException e){}
bot.mouseRelease(InputEvent.BUTTON1_MASK);
Eclipse swingcoder
plug-in:
5.8. Tests coverage
Several existing tools:
6. Application on agile project
6.1. From To Be Done
to On going
6.2. New branch (if new feature)
bruel (master) $ git checkout -b US-15378
Switched to a new branch 'US-15378'
bruel (US-15378) $
6.3. Write a failing test
6.3.1. Step 0 : Understand
Let’s take one example: Pile
class.
Rappels sur les propriétés d’une
Pile (opérations)
CreerPile : -> Pile estVide : Pile -> Booleen Empiler : Pile * Element -> Pile Depiler : Pile -> Pile Sommet : Pile -> Elément |
Rappels sur les propriétés d’une
Pile (préconditions)
Sommet(p) valide Si et Seulement Si estVide(p) == FAUX Depiler(p) valide Si et Seulement Si estVide(p) == FAUX |
Rappels sur les propriétés d’une
Pile (axiomes)
(1) estVide(CreerPile()) (2) estVide(Empiler(p,e)) == FAUX (3) estVide(Depiler(Empiler(p,e))) Si et Seulement Si estVide(p) (4) Sommet(Empiler(p,e)) == e (5) !estVide(p) => Sommet(Depiler(Empiler(p,e))) == Sommet(p) |
6.3.2. Step 1 : write a simple test
import junit.textui.TestRunner;
import junit.framework.TestSuite;
import junit.framework.TestCase;
public class PileTest extends TestCase {
public void test_type_new_Pile() throws Exception {
Pile pile = new Pile() ;
assertEquals("new Pile() retourne une Pile", "Pile", pile.getClass().getName());
}
}
6.3.3. Step 1' : improved with a main
For those who really need to execute a main
:
public class PileTest extends TestCase {
static int totalAssertions = 0;
static int bilanAssertions = 0;
public void test_type_new_Pile() throws Exception {
Pile pile = new Pile() ;
totalAssertions++ ;
assertEquals("new Pile() retourne une Pile", "Pile", pile.getClass().getName());
bilanAssertions++ ;
}
public static void main(String[] args) {
junit.textui.TestRunner.run(new TestSuite(PileTest.class));
if (bilanAssertions == totalAssertions) { System.out.print("Bravo !"); }
System.out.println(" "+bilanAssertions+"/"+totalAssertions+" assertions vérifiées");
} // fin main
} // fin PileTest
Executing the test as Java program:
...
Time: 0,005
OK (3 tests)
Bravo ! 3/3 assertions vérifiées
On command line:
javac -cp .;junit.jar PileTest.java
6.3.4. Step 2 : write a test that passes
public void test_type_empiler() throws Exception {
Pile pile = new Pile() ;
assertEquals("empiler(pile,'XXX') retourne une Pile", "Pile", pile.empiler("XXX").getClass().getName());
}
public class Pile {
public Object empiler(String string) {
// TODO Auto-generated method stub
return this;
}
}
6.3.5. Step 2 : writing a failing test
public void test_axiome1() {
Pile pile = new Pile() ;
assertTrue("Une nouvelle pile est vide", pile.estVide(pile));
}
public boolean estVide(Pile pile) {
// TODO Auto-generated method stub
return false;
}
Junit runs only functions that start with
Nowadays, we use the
|
6.3.6. Step 3 : Make the test succeed
public boolean estVide(Pile pile) {
// Smartly modified by JMB to pass the test!
return true;
}
Of course our code is wrong! Here is a better one:
|
6.4. Attempt to merge
bruel (US-15378) $ git commit -am "Adding push feature. Tests OK"
[US-15378 78f3242] Adding push feature. Tests OK
1 file changed, 2 insertions(+), 3 deletions(-)
bruel (US-15378) $ git checkout devs
Switched to branch 'devs'
bruel (devs) $ git merge US-15378
6.5. Commit & Push into devs
branch
bruel (devs) $ git commit -am "..."
...
bruel (devs) $ git push origin devs
...
bruel (devs) $ git branch -D US-15378
Deleted branch US-15378 (was f392a73).