image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Programmation par contrat

Pour chacune des méthodes d'une classe, on écrit une Javadoc décrivant son comportement (arguments attendus, action de la méthode, valeur de retour, éventuelles exceptions levées).
Au niveau du code, on vérifie :

Exemple : suppression des doublons d'une liste

package fr.upem.jacosa.safety;

import java.util.Iterator;
import java.util.List;

public class DuplicateRemover
{
	/** 
	 * Determine if a list is sorted (and does not contain null references)
	 * @param list the list to check
	 * @return if the list is sorted according to the default comparator
	 */
	public static <T extends Comparable<T>> boolean isSorted(List<T> list)
	{
		T previous = null;
		for (T element: list)
			if (element == null || (previous != null && previous.compareTo(element) > 0))
				return false;
			else
				previous = element;
		return true;
	}
	
	/**
	 * Get the number of duplicates in a sorted list (without null references)
	 * @param list the sorted list
	 * @return the number of duplicates
	 */
	public static <T extends Comparable<T>> int getDuplicateNumber(List<T> list)
	{
		assert(isSorted(list));
		T previous = null;
		int duplicates = 0;
		for (T element: list)
			if (previous != null && previous.equals(element))
				duplicates++;
			else
				previous = element;
		return duplicates;
	}
	
	/**
	 * Remove the duplicates from a sorted list
	 * @param list the sorted list (without null references)
	 */
	public static <T extends Comparable<T>> void removeDuplicates(List<T> list)
	{
		assert(isSorted(list)):"The list is not sorted (or contain null references)";
		int initialSize = list.size();
		T previous = null;
		int duplicates = 0;
		for (Iterator<T> it = list.iterator(); it.hasNext(); )
		{
			T element = it.next();
			if (previous != null && element.equals(previous))
			{
				it.remove();
				duplicates++;
			}
			else
				previous = element;
			assert(initialSize == list.size() + duplicates);
		}
		assert(getDuplicateNumber(list) == 0);
	}
}

Les assertions (Java 1.5)

Tests unitaires

Principe général de JUnit

Voici un squelette de classe pour réaliser des tests :

public class MyTests {
	
	@BeforeAll
	public static void initializeAllTests() {
		// ...
	}
	
	@BeforeEach
	public void initializeEachTest() {
		// ...
	}
	
	@Test
	@Tag("important")
	public void firstTest() {
		// ... make some assertions
	}
	
	@Test
	@Tag("futile")
	@Timeout(value = 10, unit = TimeUnit.SECONDS) // protection against infinite loops
	public void secondTest() {
		// ... other assertions
	}
	
	@AfterEach
	public void doAfterEachTest() {
	}
	
	@AfterAll
	public static void afterAllTests() {
	}
}

Tests paramétrés

Quelques exemples de tests paramétrés :

package fr.upem.jacosa.safety;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.IntStream;

/** Introduce some parameterized tets using JUnit */
public class SomeParameterizedTests {

    /** Is the indexOf method of string working correctly?
     *  We use a CSV source to test it with some examples
     */
    @DisplayName("Search a needle into a haystack")
    @ParameterizedTest
    @CsvSource({"bonjour,jour,3", "au revoir,rev,3", "foobar,foo,0"})
    void testStringIndexOf(String s, String needle, String expectedIndex) {
        Assertions.assertEquals(expectedIndex, s.indexOf(needle));
    }

    /** Test if the method Math.sqrt works flawlessly with some integers */
    @DisplayName("Test the Math.sqrt method")
    @MethodSource("intGenerator")
    void testSqrt(int i) {
        Assertions.assertEquals(i, Math.sqrt(i*i));
    }

    /** Return the integers from 0 to 1000 */
    public static IntStream intGenerator() {
        return IntStream.iterate(0, x -> x + 1).limit(1000);
    }
}

Testons le supprimeur de doublons

package fr.upem.jacosa.safety;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;


import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class DuplicateRemoverTest
{
	public static final int LIST_SIZE = 128;
	public static final int MAX_NUMBER = 10;
	
	private List<Integer> list = null;
	private static Random random = null;
	
	@BeforeAll
	public static void initClass()
	{
		random = new Random();
	}
	
	@BeforeEach
	public void initList()
	{
		list = new ArrayList<Integer>();
		for (int i = 0; i < LIST_SIZE; i++)
			list.add(random.nextInt(MAX_NUMBER+1));
		Collections.sort(list);
	}
	
	@Test
	public void testDuplicateRemover()
	{
		Assertions.assertTrue(DuplicateRemover.isSorted(list), "Initial list not sorted");
		int initialSize = list.size();
		int duplicateNumber = DuplicateRemover.getDuplicateNumber(list);
		DuplicateRemover.removeDuplicates(list);
		Assertions.assertTrue( initialSize == list.size() + duplicateNumber, "Incoherent number of removed duplicates");
		Assertions.assertTrue(DuplicateRemover.isSorted(list), "Not sorted list");
	}
	
	@Test
	public void testDuplicateRemover2()
	{
		Set<Integer> set = new HashSet<Integer>(list);
		DuplicateRemover.removeDuplicates(list);
		Set<Integer> set2 = new HashSet<Integer>(list);
		Assertions.assertFalse(set2.size() > set.size(), "Some elements have been added");
		Assertions.assertFalse(set2.size() < set.size(), "Some elements have been removed");
		Assertions.assertEquals(set, set2, "Some elements have been modified");
	}
}

Lançons les tests avec JUnit

test {
    useJUnitPlatform()
}