CentraleSupélecDépartement informatique
Plateau de Moulon
3 rue Joliot-Curie
F-91192 Gif-sur-Yvette cedex
Une solution possible de l'exercice tests

Solution tests

3. Premiers tests

Date.java (version incorrecte)

package gloo.date;

public class Date {

    private int jour;
    private int mois;
    private int annee;

    public Date( int jour, int mois, int annee ) {
        super();
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    public Date lendemain() {
        return new Date( jour + 1, mois, annee );
    }
}

TestDateLendemainSpec.java

package gloo.date.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import gloo.date.Date;

class TestDateLendemainSpec {

    @Test
    void testLendemainNotNull() {
        Date date = new Date( 18, 1, 2022 );
        assertNotNull( date.lendemain() );
    }

    @Test
    void testLendemainNotSame() {
        Date date = new Date( 18, 1, 2022 );
        assertNotSame( date, date.lendemain() );
    }

    @Test
    void testLendemainSimple() {
        Date today = new Date( 18, 1, 2022 );
        Date tomorrow = new Date( 19, 1, 2022 );
        assertEquals( tomorrow, today.lendemain() );
    }
}

Date.java (version corrigée)

package gloo.date;

public class Date {

    private int jour;
    private int mois;
    private int annee;

    public Date( int jour, int mois, int annee ) {
        super();
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    // hashCode() n'est pas utilisé dans cet exercice, mais si on demande à Eclipse de 
    // générer equals(), il génère aussi hashcode().
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + annee;
        result = prime * result + jour;
        result = prime * result + mois;
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if( this == obj ) return true;
        if( obj == null ) return false;
        if( getClass() != obj.getClass() ) return false;
        Date other = ( Date ) obj;
        if( annee != other.annee ) return false;
        if( jour != other.jour ) return false;
        if( mois != other.mois ) return false;
        return true;
    }

    public Date lendemain() {
        return new Date( jour + 1, mois, annee );
    }
}

4. Prise en compte de la durée des mois

Date.java

package gloo.date;

public class Date {

    private static final int NOMBRE_JOURS_MAX = 31;
    private static final int NOMBRE_MOIS = 12;

    private int jour;
    private int mois;
    private int annee;

    public Date( int jour, int mois, int annee ) {
        super();
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + annee;
        result = prime * result + jour;
        result = prime * result + mois;
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if( this == obj ) return true;
        if( obj == null ) return false;
        if( getClass() != obj.getClass() ) return false;
        Date other = ( Date ) obj;
        if( annee != other.annee ) return false;
        if( jour != other.jour ) return false;
        if( mois != other.mois ) return false;
        return true;
    }

    public Date lendemain() {
        int j = jour + 1;
        int m = mois;
        int a = annee;
        if( j > nombreJoursDansMois( mois, annee )) {
            j = 1;
            m += 1;
            if( m > NOMBRE_MOIS ) {
                m = 1;
                a += 1;
            }
        }
        return new Date( j, m, a );
    }

    private static int nombreJoursDansMois( int mois ) {
        return switch( mois ) {
            case 1, 3, 5, 7, 8, 10, 12 -> NOMBRE_JOURS_MAX;
            case 2 -> NOMBRE_JOURS_MAX - 3;
            case 4, 6, 9, 11 -> NOMBRE_JOURS_MAX - 1;
            default -> throw new IllegalArgumentException( "Unexpected value: " + mois );
        };
    }
}

TestDateLendemainFinMois.java

package gloo.date.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import gloo.date.Date;

class TestDateLendemainFinMois {

    @Test
    void testDateFinJanvier() {
        Date actual = new Date( 31, 1, 2022 );
        Date expected = new Date( 1, 2, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinFevrier() {
        Date actual = new Date( 28, 2, 2022 );
        Date expected = new Date( 1, 3, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinMars() {
        Date actual = new Date( 31, 3, 2022 );
        Date expected = new Date( 1, 4, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinAvril() {
        Date actual = new Date( 30, 4, 2022 );
        Date expected = new Date( 1, 5, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinMai() {
        Date actual = new Date( 31, 5, 2022 );
        Date expected = new Date( 1, 6, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinJuin() {
        Date actual = new Date( 30, 6, 2022 );
        Date expected = new Date( 1, 7, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinJuillet() {
        Date actual = new Date( 31, 7, 2022 );
        Date expected = new Date( 1, 8, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinAout() {
        Date actual = new Date( 31, 8, 2022 );
        Date expected = new Date( 1, 9, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinSeptembre() {
        Date actual = new Date( 30, 9, 2022 );
        Date expected = new Date( 1, 10, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinOctobre() {
        Date actual = new Date( 31, 10, 2022 );
        Date expected = new Date( 1, 11, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinNovembre() {
        Date actual = new Date( 30, 11, 2022 );
        Date expected = new Date( 1, 12, 2022 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testDateFinDecembre() {
        Date actual = new Date( 31, 12, 2022 );
        Date expected = new Date( 1, 1, 2023 );
        assertEquals( expected, actual.lendemain() );
    }
}

5. Prise en compte des années bisextiles

Date.java

package gloo.date;

public class Date {

    private static final int NOMBRE_JOURS_MAX = 31;
    private static final int NOMBRE_MOIS = 12;

    private int jour;
    private int mois;
    private int annee;

    public Date( int jour, int mois, int annee ) {
        super();
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + annee;
        result = prime * result + jour;
        result = prime * result + mois;
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if( this == obj ) return true;
        if( obj == null ) return false;
        if( getClass() != obj.getClass() ) return false;
        Date other = ( Date ) obj;
        if( annee != other.annee ) return false;
        if( jour != other.jour ) return false;
        if( mois != other.mois ) return false;
        return true;
    }

    public Date lendemain() {
        int j = jour + 1;
        int m = mois;
        int a = annee;
        if( j > nombreJoursDansMois( mois, annee )) {
            j = 1;
            m += 1;
            if( m > NOMBRE_MOIS ) {
                m = 1;
                a += 1;
            }
        }
        return new Date( j, m, a );
    }

    private static int nombreJoursDansMois( int mois, int annee ) {
        return switch( mois ) {
            case 1, 3, 5, 7, 8, 10, 12 -> NOMBRE_JOURS_MAX;
            case 2 -> NOMBRE_JOURS_MAX - ( estBissextile( annee ) ? 2 : 3 );
            case 4, 6, 9, 11 -> NOMBRE_JOURS_MAX - 1;
            default -> throw new IllegalArgumentException( "Unexpected value: " + mois );
        };
    }

    private static boolean estBissextile( int annee ) {
        return annee % 400 == 0 || (( annee % 4 == 0 ) && ( annee % 100 != 0 ));
    }
}

TestAnneeBissextile.java

package gloo.date.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import gloo.date.Date;

class TestAnneeBissextile {

    @Test
    void testAnneeBissectile2008() {
        Date actual = new Date( 28, 2, 2008 );
        Date expected = new Date( 29, 2, 2008 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testAnneeNonBissectile1900() {
        Date actual = new Date( 28, 2, 1900 );
        Date expected = new Date( 1, 3, 1900 );
        assertEquals( expected, actual.lendemain() );
    }

    @Test
    void testAnneeBissectile2000() {
        Date actual = new Date( 28, 2, 2000 );
        Date expected = new Date( 29, 2, 2000 );
        assertEquals( expected, actual.lendemain() );
    }
}

6. Validation des dates initiales

Date.java

package gloo.date;

public class Date {

    private static final int NOMBRE_JOURS_MAX = 31;
    private static final int NOMBRE_MOIS = 12;

    private int jour;
    private int mois;
    private int annee;

    public Date( int jour, int mois, int annee ) {
        super();
        verifieArguments( jour, mois, annee );
        this.jour = jour;
        this.mois = mois;
        this.annee = annee;
    }

    private static void verifieArguments( int jour, int mois, int annee ) {
        if( jour <= 0 )
            throw new IllegalArgumentException( "jour doit être >= 1" );
        // Il faut vérifier la validité de mois et annee avant d'utiliser nombreJoursDansMois
        if( mois <= 0 )
            throw new IllegalArgumentException( "mois doit être >= 1" );
        if( mois > NOMBRE_MOIS )
            throw new IllegalArgumentException( "mois doit être <= 12" );
        if( annee <= 0 )
            throw new IllegalArgumentException( "annee doit être >= 1" );
        if( jour > nombreJoursDansMois( mois, annee ))
            throw new IllegalArgumentException( "jour doit être >= 1" );
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + annee;
        result = prime * result + jour;
        result = prime * result + mois;
        return result;
    }

    @Override
    public boolean equals( Object obj ) {
        if( this == obj ) return true;
        if( obj == null ) return false;
        if( getClass() != obj.getClass() ) return false;
        Date other = ( Date ) obj;
        if( annee != other.annee ) return false;
        if( jour != other.jour ) return false;
        if( mois != other.mois ) return false;
        return true;
    }

    public Date lendemain() {
        int j = jour + 1;
        int m = mois;
        int a = annee;
        if( j > nombreJoursDansMois( mois, annee )) {
            j = 1;
            m += 1;
            if( m > NOMBRE_MOIS ) {
                m = 1;
                a += 1;
            }
        }
        return new Date( j, m, a );
    }

    private static int nombreJoursDansMois( int mois, int annee ) {
        return switch( mois ) {
            case 1, 3, 5, 7, 8, 10, 12 -> NOMBRE_JOURS_MAX;
            case 2 -> NOMBRE_JOURS_MAX - ( estBissextile( annee ) ? 2 : 3 );
            case 4, 6, 9, 11 -> NOMBRE_JOURS_MAX - 1;
            default -> throw new IllegalArgumentException( "Unexpected value: " + mois );
        };
    }

    private static boolean estBissextile( int annee ) {
        return annee % 400 == 0 || (( annee % 4 == 0 ) && ( annee % 100 != 0 ));
    }
}

TestConstructeurDate.java

package gloo.date.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import gloo.date.Date;

class TestConstructeurDate {

    @Test
    void testConstructeurDateValide() {
        assertDoesNotThrow( () -> { new Date( 18, 1, 2022 ); } );
    }

    @Test
    void testConstructeurDateInvalideJourNegatif() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( -10, 2, 2020 ); }
        );
    }

    @Test
    void testConstructeurDateInvalideJourNul() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 0, 12, 2026 ); }
        );
    }

    @Test
    void testConstructeurDateFinJanvier() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 1, 4126 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 1, 4126 ); }
            );
    }

    @Test
    void testConstructeurDateFinFevrier() {
        assertDoesNotThrow(
                () -> { new Date( 28    , 2, 1111 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 28 + 1, 2, 1111 ); }
            );
    }

    @Test
    void testConstructeurDateFinFevrierBissextile() {
        assertDoesNotThrow(
                () -> { new Date( 29    , 2, 2008 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 29 + 1, 2, 2008 ); }
            );
    }

    @Test
    void testConstructeurDateFinMars() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 3, 3 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 3, 3 ); }
            );
    }

    @Test
    void testConstructeurDateFinAvril() {
        assertDoesNotThrow(
                () -> { new Date( 30    , 4, 9999 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 30 + 1, 4, 9999 ); }
            );
    }

    @Test
    void testConstructeurDateFinMai() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 5, 5555 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 5, 5555 ); }
            );
    }

    @Test
    void testConstructeurDateFinJuin() {
        assertDoesNotThrow(
                () -> { new Date( 30    , 6, 6 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 30 + 1, 6, 6 ); }
            );
    }

    @Test
    void testConstructeurDateFinJuillet() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 7, 123 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 7, 123 ); }
            );
    }

    @Test
    void testConstructeurDateFinAout() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 8, 1 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 8, 1 ); }
            );
    }

    @Test
    void testConstructeurDateFinSeptembre() {
        assertDoesNotThrow(
                () -> { new Date( 30    , 9, 2020 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 30 + 1, 9, 2020 ); }
            );
    }

    @Test
    void testConstructeurDateFinOctobre() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 10, 4040 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 10, 4040 ); }
            );
    }

    @Test
    void testConstructeurDateFinNovembre() {
        assertDoesNotThrow(
                () -> { new Date( 30    , 11, 6060 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 30 + 1, 11, 6060 ); }
            );
    }

    @Test
    void testConstructeurDateFinDecembre() {
        assertDoesNotThrow(
                () -> { new Date( 31    , 12, 12 ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( 31 + 1, 12, 12 ); }
            );
    }

    @Test
    void testDateInvalideMoisNegatif() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 10, -12, 1515 ); }
        );
    }

    @Test
    void testDateInvalideMoisNul() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 31, 0, 999 ); }
        );
    }

    @Test
    void testDateInvalideMoisTropGrand() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 1, 13, 1 ); }
        );
    }

    @Test
    void testDateInvalideAnneeNegative() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 20, 2, -1 ); }
        );
    }

}

7. Meilleure écriture des tests

TestConstructeurDate.java

package gloo.date.test;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import gloo.date.Date;

class TestConstructeurDate {

    @Test
    void testConstructeurDateValide() {
        assertDoesNotThrow( () -> { new Date( 18, 1, 2022 ); } );
    }

    @Test
    void testConstructeurDateInvalideJourNegatif() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( -10, 2, 2020 ); }
        );
    }

    @Test
    void testConstructeurDateInvalideJourNul() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 0, 12, 2026 ); }
        );
    }

    @ParameterizedTest
    @CsvSource({ "31,  1, 4126",
                 "28,  2, 1111",
                 "29,  2, 2008",
                 "31,  3,    3",
                 "30,  4, 9999",
                 "31,  5, 5555",
                 "30,  6,    6",
                 "31,  7,  123",
                 "31,  8,    1",
                 "30,  9, 2020",
                 "31, 10, 4040",
                 "30, 11, 6060",
                 "31, 12,   12"
    })
    void testConstructeurDateFinMois( int j, int m, int a ) {
        assertDoesNotThrow(
                () -> { new Date( j,     m, a ); }
        );
        assertThrows(
                IllegalArgumentException.class,
                () -> { new Date( j + 1, m, a ); }
            );
    }

    @Test
    void testDateInvalideMoisNegatif() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 10, -12, 1515 ); }
        );
    }

    @Test
    void testDateInvalideMoisNul() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 31, 0, 999 ); }
        );
    }

    @Test
    void testDateInvalideMoisTropGrand() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 1, 13, 1 ); }
        );
    }

    @Test
    void testDateInvalideAnneeNegative() {
        assertThrows(
            IllegalArgumentException.class,
            () -> { new Date( 20, 2, -1 ); }
        );
    }

}


© 2020-22 CentraleSupélec