Tutoriel avancé sur AssertJ-DB : les concepts et assertions

Régis POUILLER

AssertJ-DB peut être utilisé dans les tests unitaires pour avoir une solution de remplacement à DBUnit, en fournissant des assertions pour tester la valeur de données. Il permet donc de valider l'état de la base de données à la fin d'un test.

Suite à l'article d'introduction « Tutoriel sur une alternative à DBUnit : AssertJ-DB en 5 minutes », cet article présente les concepts et assertions de AssertJ-DB.

Une discussion a été ouverte pour les commentaires sur la publication de cet article : [ Commentez ♪

- Titre : Tutoriel avancé sur AssertJ-DB : les concepts et assertions

- Auteur : Régis POUILLER

- Parution : 27 novembre 2015

- Licence : Copyright ® 2015 Régis POUILLER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Concepts d'AssertJ-DB

- II-A. La connexion à la base de données

- II-A-1. DataSource : un classique du monde Java

- II-A-2. Source : une solution propre à AssertJ-DB

- II-B. Les éléments de la base de données

- II-B-1. Table : une table dans la base

- II-B-2. Request : le résultat d'une requête Select

- II-B-3. Changes : les changements dans les données dans un intervalle de temps

- II-B-4. Change : un de ces changements

- II-B-5. Row : un enregistrement

- II-B-6. Column : une colonne

- II-B-7. Value : une valeur

- II-B-8. Synthèse des éléments de la base de données

- II-C. Les différents types

- II-C-1. DataType : le type de données

- II-C-2. ChangeType : le type de changement

- II-C-3. ValueType : le type de valeur

- II-D. La navigation

- II-D-1. Les éléments racine : point de départ

- II-D-2. La navigation depuis une table ou une requête

- II-D-2-a. La navigation « descendante »

- II-D-2-a-i. Vers un enregistrement

- II-D-2-a-ii. Vers une colonne

- II-D-2-a-iii. Vers une valeur

- II-D-2-b. La navigation « remontante »

- II-D-2-b-i. Vers la colonne ou l'enregistrement

- II-D-2-b-ii. Vers la table ou requête

- II-D-3. La navigation depuis des changements

- II-D-3-a. La navigation « descendante »

- II-D-3-a-i. Vers des changements

- II-D-3-a-ii. Vers un changement

- II-D-3-a-iii. Vers un enregistrement

- II-D-3-a-iv. Vers une colonne

- II-D-3-a-v. Vers une valeur

- II-D-3-b. La navigation « remontante »

- II-D-3-b-i. Vers la colonne ou l'enregistrement

- II-D-3-b-ii. Vers le changement

- II-D-3-b-iii. Vers les changements

- II-D-4. Enchaînement de méthodes de navigation « descendante »

- II-E. Les DateValue, TimeValue et DateTimeValue

- II-E-1. Les DateValue

- II-E-2. Les TimeValue

- II-E-3. Les DateTimeValue

- II-F. Les descriptions par défaut

- III. Exemples d'assertion

- III-A. Assertion sur les nombres de changements, colonnes et enregistrements

- III-A-1. Assertion sur les nombres de changements

- III-A-2. Assertion sur les nombres de colonnes

- III-A-3. Assertion sur les nombres d'enregistrements

- III-B. Assertion sur les changements

- III-B-1. Assertion sur le type de changement

- III-B-2. Assertion sur le type de données

- III-B-3. Assertion sur les clés primaires

- III-B-4. Assertion sur l'existence d'un enregistrement

- III-B-5. Assertion sur les colonnes modifiées

- III-C. Assertion sur les valeurs

- III-C-1. Assertion sur le type de valeur

- III-C-2. Assertion sur la nullité d'une valeur

- III-C-3. Assertion sur l'égalité d'une valeur

- III-C-4. Assertion sur la non-égalité d'une valeur

- III-C-5. Assertion sur la chronologie d'une valeur

- III-C-6. Assertion sur la comparaison d'une valeur

- III-D. Assertion sur l'égalité avec les valeurs d'un enregistrement

- III-E. Assertion sur les colonnes

- III-E-1. Assertion sur le nom des colonnes

- III-E-2. Assertion sur le type de valeur d'une colonne

- III-E-3. Assertion sur la nullité des valeurs d'une colonne

- III-E-4. Assertion sur l'égalité des valeurs d'une colonne

- IV. Conclusion

- V. Remerciements

I - Introduction

Lors de l'article « Tutoriel sur une alternative à DBUnit : AssertJ-DB en 5 minutes », vous avez pu avoir un aperçu de AssertJ-DB.

Bien que AssertJ-DB permette simplement de réaliser des tests unitaires avec une base de données, vous allez pouvoir découvrir avec cet article les différents concepts d' ainsi que ses possibilités d'assertions. Toutes ces choses sont très simples, mais en avoir une bonne connaissance aide à utiliser au mieux .

II - Concepts d'AssertJ-DB

AssertJ-DB comporte quelques concepts (connexion à la base de données, éléments de la base de données, navigation…). Ces concepts ne sont pas compliqués, mais il est important de les connaître pour bien utiliser .

II-A - La connexion à la base de données

AssertJ-DB travaille exclusivement avec une base de données. Il est donc nécessaire d'avoir un moyen de connexion avec une base de données. Cette connexion peut s'effectuer par un objet DataSource ou un objet Source (propre à AssertJ-DB).

Pour les utilisateurs de DBSetup, c'est le concept équivalent aux Destination.

II-A-1 - DataSource : un classique du monde Java

La classe DataSource est un classique pour toute personne avec un peu d'expérience en Java. Donc, l'article ne développera pas plus ce concept. L'important est juste de connaître que c'est un moyen de connexion possible.

Pour les utilisateurs de DBSetup, cela correspond aux DataSourceDestination.

II-A-2 - Source : une solution propre à AssertJ-DB

Pour les environnements de tests sans DataSource disponible, il existe un autre moyen de connexion : Source.

Source prend en paramètres de constructeur : l'URL de la base de données, l'utilisateur et le mot passe de connexion.

Exemple d'instanciation de Source

// Instanciation d'un objet Source équivalent à l'objet DataSource du premier exemple
Source source = new Source("jdbc:h2:mem:test", "user", "password");

Pour les utilisateurs de DBSetup, cela correspond aux DriverManagerDestination.

Il est possible de consulter plus d'informations dans la documentation officielle de Source.

II-B - Les éléments de la base de données

II-B-1 - Table : une table dans la base

Un objet Table correspond tout simplement à une table dans la base de données. Sa construction nécessite au minimum un moyen de connexion à la base et le nom de la table.

Exemples d'instanciation de Table

// Les deux instanciations ci-dessous correspondent à des objets Table
// sur la table "membres" qui est dans la base H2 en mémoire
// Instanciation d'un objet Table grâce à un objet DataSource
Table table1 = new Table(dataSource, "membres");
// Instanciation d'un objet Table grâce à un objet Source
Table table2 = new Table(source, "membres");

Table comporte deux autres paramètres optionnels, mais qui s'ajoutent ensemble. Le premier paramètre supplémentaire indique les colonnes à inclure dans le traitement; tandis que le second paramètre supplémentaire indique les colonnes à exclure. Si un de ces paramètres n'est pas à prendre en compte, il est renseigné à null.

Exemples d'instanciation de Table

// Instanciation équivalente à table1 ou table2
Table table3 = new Table(source, "membres", null, null);
// table4 ne contient que les colonnes "id" et "nom" et aucune autre
Table table4 = new Table(source, "membres", new String[] { "id", "nom" }, null);
// table5 contient toutes les colonnes à l'exception de "date_naissance"
Table table5 = new Table(source, "membres", null, new String[] { "date_naissance" });
// table6 ne contient que la colonne "nom" car la colonne "id" est incluse et exclue
Table table6 = new Table(source, "membres", new String[] { "id", "nom" }, new String[] { "id" });

L'instance table4 ne contient que les colonnes ID et NOM (celles à inclure).

Représentation de table4

ID

NOM

1

Hewson

2

Evans

3

Clayton

4

Mullen

L'instance table5 contient toutes les colonnes à l'exception de DATE_NAISSANCE (celle à exclure).

Représentation de table5

ID

NOM

PRENOM

SURNOM

TAILLE

1

Hewson

Paul David

Bono

1,75

2

Evans

David Howell

The Edge

1,77

3

Clayton

Adam

1,78

4

Mullen

Larry

1,70

L'instance table6 contient uniquement NOM (ID était à inclure et à exclure).

Représentation de table6

NOM

Hewson

Evans

Clayton

Mullen

Il est possible de consulter plus d'informations dans la documentation officielle de Table.

II-B-2 - Request : le résultat d'une requête Select

Un objet Request correspond tout simplement à une requête SQL dans la base de données. Sa construction nécessite au minimum un moyen de connexion à la base et le SQL de la requête.

Exemples d'instanciation de Request

// Les deux instanciations ci-dessous correspondent à des objets Request
// dans la base H2 en mémoire
// Instanciation d'un objet Request grâce à un objet DataSource
Request request1 = new Request(dataSource,
"select * from membres where surnom is not null");
// Instanciation d'un objet Request grâce à un objet Source
Request request2 = new Request(source,
"select * from membres where surnom is not null");

Cela donne la représentation suivante :

Représentation de request1 ou request2

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

Le constructeur Request comporte également des paramètres optionnels qui sont utilisés comme paramètres pour l'exécution de la requête SQL.

Exemples d'instanciation de Request

// Instanciation d'un objet Request avec un paramètre
Request request3 = new Request(dataSource,
"select nom nom_membre, prenom from membres where taille > ?",
1.75);
// Instanciation d'un objet Request avec deux paramètres
Request request4 = new Request(source,
"select nom, prenom, surnom from membres where taille > ? and prenom like ?",
1.75,
"?vid%");

L'instance request3 contient les enregistrements respectant la condition (une fois le paramètre de requêtes remplacé par 1.75) et les colonnes correspondent aux colonnes de la requête (en tenant compte des alias).

Représentation de request3

NOM_MEMBRE

PRENOM

Evans

David Howell

Clayton

Adam

L'instance request4 fonctionne sur le même principe (il y a juste deux paramètres de requêtes au lieu d'un seul).

Représentation de request4

NOM

PRENOM

SURNOM

Evans

David Howell

The Edge

Il est possible de consulter plus d'informations dans la documentation officielle de Request.

II-B-3 - Changes : les changements dans les données dans un intervalle de temps

Un objet Changes correspond aux changements de valeur des données durant un intervalle de temps défini par un point de début et un point de fin. Concrètement l'état des données à l'instant du point de début est comparé avec l'état des données du point de fin. Ces changements peuvent être sur une/des table(s) (seuls les changements de valeurs dans la/les table(s) sont pris en compte), une requête (seuls les changements de valeurs dans le résultat de la requête sont pris en compte) ou un moyen de connexion à la base de données (les changements de valeurs dans l'ensemble de la base de données sont pris en compte).

Voici l'état des tables au point de départ :

Table GROUPES

ID

NOM

1

The Police

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

6

Copeland

Stewart Armstrong

16/07/1952

1,87

7

Padovani

Henry

13/10/1950

Table APPARTENANCES

ID_GROUPE

ID_MEMBRE

1

5

1

6

1

7

Entre le point de départ et le point de fin, les requêtes suivantes sont exécutées.

Requêtes SQL des modifications entre le point de début et le point de fin

delete from appartenances where id_membre = 7;
delete from membres where id = 7;
insert into membres(id, nom, prenom, date_naissance) values(8, 'Summers', 'Andrew James', PARSEDATETIME('31/12/1942', 'dd/MM/yyyy'));
insert into appartenances values(1, 8);
update membres set surnom = 'Sting' where id = 5;

Voici donc l'état des tables au point de fin :

- la table GROUPES n'est pas modifiée :

Table GROUPES

ID

NOM

1

The Police

- dans la table MEMBRES, l'enregistrement avec ID 5 est modifié (colonne SURNOM), l'enregistrement avec ID 7 est supprimé et l'enregistrement avec ID 8 est créé.

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

6

Copeland

Stewart Armstrong

16/07/1952

1,87

8

Summers

Andrew James

31/12/1942

- dans la table MEMBRES, l'enregistrement avec ID_GROUPE 1 et ID_MEMBRE 7 est supprimé et l'enregistrement avec ID_GROUPE 1 et ID_MEMBRE 8 est créé.

Table APPARTENANCES

ID_GROUPE

ID_MEMBRE

1

5

1

6

1

8

Exemples d'instanciation de Changes

private void faireChangements() throws SQLException {
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("delete from appartenances where id_membre = 7;");
statement.executeUpdate("delete from membres where id = 7;");
statement.executeUpdate("insert into membres(id, nom, prenom, date_naissance) values(8, 'Summers', 'Andrew James', PARSEDATETIME('31/12/1942', 'dd/MM/yyyy'));");
statement.executeUpdate("insert into appartenances values(1, 8);");
statement.executeUpdate("update membres set surnom = 'Sting' where id = 5;");
}
}
}

@Test
public void test() throws SQLException {
Table table1 = new Table(dataSource,
"MEMBRES");
Changes changes1 = new Changes(table1);
//Changements sur la table MEMBRES sans tenir compte de la colonne SURNOM
Table table2 = new Table(dataSource,
"MEMBRES",
null,
new String[] { "SURNOM" });
Changes changes2 = new Changes(table2);
// Changements dans le résultat d'une requête
Request request1 = new Request(dataSource,
"select groupes.nom nom_groupe, " +
"       membres.nom nom_membre, " +
"       membres.surnom, " +
"       membres.id id_membre " +
" from membres, " +
"      groupes, " +
"      appartenances " +
" where membres.id = appartenances.id_membre " +
" and groupes.id = appartenances.id_groupe " +
" and membres.taille is not null");
Changes changes3 = new Changes(request1);
// Changements dans le résultat d'une requête
Request request2 = new Request(dataSource,
"select groupes.nom as nom_groupe, " +
"       membres.nom nom_membre, " +
"       membres.surnom, " +
"       membres.id id_membre " +
" from membres, " +
"      groupes, " +
"      appartenances " +
" where membres.id = appartenances.id_membre " +
" and groupes.id = appartenances.id_groupe " +
" and membres.taille is not null");
request2.setPksName("id_membre");
Changes changes4 = new Changes(request2);
// Changements sur l'ensemble de la base de données
Changes changes5 = new Changes(dataSource);
changes1.setStartPointNow();
changes2.setStartPointNow();
changes3.setStartPointNow();
changes4.setStartPointNow();
changes5.setStartPointNow();
faireChangements();
changes1.setEndPointNow();
changes2.setEndPointNow();
changes3.setEndPointNow();
changes4.setEndPointNow();
changes5.setEndPointNow();

...
}

Les changements sont triés par type de changement (CREATION, MODIFICATION puis DELETION) puis par nom de table et par valeur de clé primaire (à défaut par valeur des colonnes en tenant compte de l'ordre des colonnes).

changes1 contient uniquement les changements dans la table MEMBRES.

Représentation de changes1

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

changes2 contient les changements dans la table MEMBRES, mais pas la modification, car elle est sur la colonne SURNOM qui a été exclue de table2.

Représentation de changes2

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

DELETION

MEMBRES

7

ID

NOM

PRENOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

changes3 contient les changements dans la requête, mais la modification est considérée comme un effacement et une création (request1 n'a pas de clé primaire déclarée donc AssertJ-DB ne sait pas comment déterminer quels enregistrements sont les mêmes avec modification).

Représentation de changes3

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

NOM_GROUPE

NOM_MEMBRE

SURNOM

ID_MEMBRE

Au point de départ

Au point de fin

The Police

Sumner

Sting

5

DELETION

NOM_GROUPE

NOM_MEMBRE

SURNOM

ID_MEMBRE

Au point de départ

The Police

Sumner

5

Au point de fin

changes4 contient le changement dans la requête en tant que modification, car ID_MEMBRE a été déclaré comme clé primaire.

Représentation de changes4

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

MODIFICATION

5

NOM_GROUPE

NOM_MEMBRE

SURNOM

ID_MEMBRE

Au point de départ

The Police

Sumner

5

Au point de fin

The Police

Sumner

Sting

5

changes5 contient tous les changements qui se sont produits dans la base de données entre le point de début et le point de fin.

Représentation de changes5

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Il est possible de consulter plus d'informations dans la documentation officielle de Changes.

II-B-4 - Change : un de ces changements

Un objet Change correspond à un changement parmi les changements d'un Changes.

L'objet Changes changes5 (présenté dans le chapitre II-B-3 précédent) contient 5 changements (Change).

Un Change contient les informations de type de changement, le nom de la table, la valeur de la clé primaire ainsi que les enregistrements au point de début et au point de fin.

Dans l'exemple ci-dessous, le changement sélectionné (en gras sur fond coloré) est le deuxième changement (Change) de changes5 ne contient pas d'enregistrement de début, car il s'agit d'une création.

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Il est possible de consulter plus d'informations dans la documentation officielle de Change.

II-B-5 - Row : un enregistrement

Un objet Row correspond à un enregistrement : un enregistrement dans une table, dans le résultat d'une requête ou l'enregistrement au point de début ou de fin d'un changement.

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est le troisième enregistrement (Row) de table4.

ID

NOM

1

Hewson

2

Evans

3

Clayton

4

Mullen

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est le premier enregistrement (Row) de request1.

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est l'enregistrement au point de fin (Row) du troisième changement de changes5.

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Il est possible de consulter plus d'informations dans la documentation officielle de Row.

II-B-6 - Column : une colonne

Un objet Column correspond à une colonne : une colonne dans une table, dans le résultat d'une requête ou dans un changement.

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la première colonne (Column) de table4.

ID

NOM

1

Hewson

2

Evans

3

Clayton

4

Mullen

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la troisième colonne (Column) de request1.

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la cinquième colonne (Column) du troisième changement de changes5.

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Il est possible de consulter plus d'informations dans la documentation officielle de Column.

II-B-7 - Value : une valeur

Une valeur peut être obtenue depuis un objet Row ou un objet Column.

Dans l'exemple ci-dessous, la valeur sélectionnée (en gras sur fond coloré) est à la fois la première valeur du troisième enregistrement de table4 et la troisième valeur de la première colonne de table4.

ID

NOM

1

Hewson

2

Evans

3

Clayton

4

Mullen

Dans l'exemple ci-dessous, la valeur sélectionnée (en gras sur fond coloré) est à la fois la troisième valeur du premier enregistrement de request1 et la première valeur de la troisième colonne de request1.

Représentation de la troisième colonne (en gras sur fond coloré) de request1

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

Dans l'exemple ci-dessous, la valeur sélectionnée (en gras sur fond coloré) est à la fois la sixième valeur de l'enregistrement au point de fin du troisième changement de changes5 et la valeur au point de fin de la sixième colonne du troisième changement de changes5.

Représentation de la cinquième colonne (en gras sur fond coloré) du troisième changement de changes5

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Il est possible de consulter plus d'informations dans la documentation officielle de Value.

II-B-8 - Synthèse des éléments de la base de données

Comme vu dans ce chapitre, les éléments Change, Row, Column et Value sont plus ou moins directement des sous-éléments de Table, Request et Changes.

Inversement, Table, Request et Changes sont des éléments racine. Cela signifie que les assertions auront toujours comme point de départ un de ces trois éléments.

Table et Request sont des éléments racine de données. Cela signifie que ces éléments ont des correspondances dans la base de données. Tandis que Changes correspondra à des changements dans une ou plusieurs Tables ou dans le résultat d'une Request.

II-C - Les différents types

II-C-1 - DataType : le type de données

Le type de données (DataType) est une information permettant de distinguer les deux types d'éléments de données :

- TABLE pour une table de données ;

- REQUEST pour le résultat d'une requête.

Il est possible de consulter plus d'informations dans la documentation officielle de DataType.

II-C-2 - ChangeType : le type de changement

Le type de changement (ChangeType) est une information permettant de distinguer les trois types de changement sur les données correspondant aux trois opérations SQL de modification :

- CREATION pour une requête SQL insert;

- MODIFICATION pour une requête SQL update;

- DELETION pour une requête SQL delete.

Il est possible de consulter plus d'informations dans la documentation officielle de ChangeType.

II-C-3 - ValueType : le type de valeur

Le type de valeur (ValueType) est une information permettant de distinguer les différents types de valeur correspondant aux différentes classes retournées lors de la récupération des données depuis la base de données :

- BYTES si JDBC retourne un tableau d'octets (byte[]) ;

- BOOLEAN si JDBC retourne un java.lang.Boolean ;

- TEXT si JDBC retourne un java.lang.String ;

- DATE si JDBC retourne un java.sql.Date ;

- TIME si JDBC retourne un java.sql.Time ;

- DATE_TIME si JDBC retourne un java.sql.Timestamp ;

- NUMBER si JDBC retourne un java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float ou java.math.BigDecimal ;

- NOT_IDENTIFIED pour les autres cas (par exemple si la valeur est null).

Il est possible de consulter plus d'informations dans la documentation officielle de ValueType.

II-D - La navigation

II-D-1 - Les éléments racine : point de départ

Les éléments racine Table, Request et Changes sont les points de départ de toutes les assertions.

Une assertion commence toujours avec l'appel à la méthode statique assertThat(...). Cette méthode appartient à la classe Assertions.

Pour utiliser les assertions, il est pratique de procéder au départ à un import statique de cette méthode. Cela rendra l'écriture/lecture du code plus simple.

Import statique de assertThat

import static org.assertj.db.api.Assertions.assertThat;

Chaque élément racine a sa méthode assertThat(...) pour commencer l'assertion :

- avec un objet Table :

assertThat avec un objet Table

assertThat(table)...       // L'assertion est pour le moment au niveau de la table

- avec un objet Request :

assertThat avec un objet Request

assertThat(request)...   // L'assertion est pour le moment au niveau de la requête

- avec un objet Changes :

assertThat avec un objet Changes

assertThat(changes)...   // L'assertion est pour le moment au niveau des changements

II-D-2 - La navigation depuis une table ou une requête

Il est possible de consulter plus d'informations dans la documentation officielle.

La navigation depuis une Table, ou une Request est similaire, car comme vu plus haut dans la présentation des éléments leur structure de sous-éléments est identique.

Les flèches continues en rouge représentent la navigation « descendante ».
Les flèches en pointillé en bleu représentent la navigation « remontante ».

Dans les sous-chapitres, nous utiliserons pour les exemples la table MEMBRES ci-dessous.

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

II-D-2-a - La navigation « descendante »

Depuis les éléments racine Table et Request, il est possible de naviguer vers une colonne (Column) ou un enregistrement (Row).
Et depuis ces sous-éléments (Column et Row), il possible de naviguer vers une valeur.

II-D-2-a-i - Vers un enregistrement

La méthode row(int index) permet de naviguer vers l'enregistrement correspondant à l'index.

Navigation vers l'enregistrement d'index 2

assertThat(table).row(2)...

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est l'enregistrement d'index 2 (comme toutes les listes en Java, cela commence à l'index 0).

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

La méthode row() permet de naviguer vers l'enregistrement suivant celui en cours.

Navigation vers l'enregistrement suivant celui d'index 2

assertThat(table).row(2).row()...

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est l'enregistrement suivant celui d'index 2 (donc celui d'index 3).

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

Si aucune navigation vers un enregistrement n'a été faite, le premier appel à la méthode row() permet de naviguer vers le premier enregistrement.

Navigation vers le premier enregistrement

assertThat(table).row()...

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est le premier enregistrement.

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

II-D-2-a-ii - Vers une colonne

La méthode column(String columnName) permet de naviguer vers la colonne correspondant au nom en paramètre.

Navigation vers la colonne « prenom »

assertThat(table).column("prenom")...

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la colonne avec le nom « PRENOM ».

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

Il existe également les méthodes column(int index) et column() qui ont un comportement similaire aux méthodes row(int index) et row().

Navigation vers la colonne d'index 1

assertThat(table).column(1)...

Navigation vers la première colonne

assertThat(table).column()...

II-D-2-a-iii - Vers une valeur

Sur les mêmes principes, il existe trois méthodes de navigation pour les valeurs :

- value(int index) pour naviguer vers une valeur à un index donné ;

- value() pour naviguer vers la valeur suivant celle en cours (ou la première si première navigation vers une valeur) ;

- value(String columnName) pour naviguer vers une valeur correspondant à une colonne donnée (navigation possible uniquement en étant sur un enregistrement).

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

Dans l'exemple ci-dessus, la valeur sélectionnée (en gras sur fond coloré) est accessible des manières suivantes (chaque ligne navigue vers la même valeur) :

Navigation en passant par un enregistrement

assertThat(table).row(2).value(2)...
assertThat(table).row(2).value("prenom")...
assertThat(table).row(2).value().value().value()...
assertThat(table).row(2).value(1).value()...

Navigation en passant par une colonne

assertThat(table).column("prenom").value(2)...
assertThat(table).column(2).value(1).value()...
assertThat(table).column(2).value().value().value()...

II-D-2-b - La navigation « remontante »

Depuis une colonne (Column) ou un enregistrement (Row) il est possible de retourner vers la Table ou la Request d'origine.
Et depuis une valeur, il est possible de retourner vers la colonne (Column) ou l'enregistrement (Row) d'origine.

II-D-2-b-i - Vers la colonne ou l'enregistrement

Si la valeur a été obtenue depuis un enregistrement, il est possible de retourner sur cet enregistrement avec la méthode returnToRow().

Retour vers un enregistrement

assertThat(table).row(2).value(2).returnToRow()...
assertThat(request).row().value("prenom").returnToRow()...

Si la valeur a été obtenue depuis une colonne, il est possible de retourner sur cette colonne avec la méthode returnToColumn().

Retour vers une colonne

assertThat(table).column("prenom").value(2).returnToColumn()...
assertThat(request).column(4).value(2).returnToColumn()...

II-D-2-b-ii - Vers la table ou requête

Si la colonne ou l'enregistrement ont été obtenus depuis une table, il est possible de retourner sur cette table avec la méthode returnToTable().

Retour vers un enregistrement

assertThat(table).row(2).returnToTable()...
assertThat(table).column("prenom").returnToTable()...

Si la colonne ou l'enregistrement ont été obtenus depuis une requête, il est possible de retourner sur cette requête avec la méthode returnToRequest().

Retour vers une colonne

assertThat(request).row().returnToRequest()...
assertThat(request).column(4).returnToRequest()...

II-D-3 - La navigation depuis des changements

Il est possible de consulter plus d'informations dans la documentation officielle.

Les flèches continues en rouge représentent la navigation « descendante ».
Les flèches en pointillé en bleu représentent la navigation « remontante ».

Dans les sous-chapitres, nous utiliserons pour les exemples les changements ci-dessous.

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

II-D-3-a - La navigation « descendante »

Depuis l'élément racine Changes, il est possible de naviguer vers un changement (Change) ou des changements (Changes).
En fait, dans ce deuxième cas, il s'agit d'un sous-ensemble de changements (on navigue de l'ensemble des changements vers les changements de création).
Depuis un changement Change, il est possible de naviguer vers une colonne (Column) ou un enregistrement (Row).
Depuis une colonne (Column ou un enregistrement Row), il possible de naviguer vers une valeur.

II-D-3-a-i - Vers des changements

Les méthodes de navigation vers un sous-ensemble de changements sont :

- onTable(String tableName) permet de naviguer vers un sous-ensemble de changements (Changes) ne contenant que des changements sur une table donnée ;

- ofCreation(), ofModification() et ofDeletion() permettent de naviguer vers un sous-ensemble de changements (Changes) ne contenant que des changements du type de changement CREATION, MODIFICATION ou DELETION ;

- ofCreationOnTable(String tableName), ofModificationOnTable(String tableName) et ofDeletionOnTable(String tableName) fonctionnent comme une combinaison de la méthode onTable(String tableName) et des méthodes ofCreation(), ofModification() et ofDeletion() afin de naviguer vers un sous-ensemble de changements (Changes) ne contenant que des changements sur une table donnée du type de changement CREATION, MODIFICATION ou DELETION.

Ci-dessous, le sous-ensemble obtenu en naviguant vers les changements d'une table donnée avec :

Navigation vers les changements sur la table MEMBRES

assertThat(changes).onTable("membres")...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Ci-dessous, le sous-ensemble obtenu en naviguant vers les changements de création avec :

Navigation vers les changements de création

assertThat(changes).ofCreation()...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

Ci-dessous, le sous-ensemble obtenu en naviguant vers les changements de création sur une table donnée avec :

Navigation vers les changements de création sur la table MEMBRES

assertThat(changes).ofCreationOnTable("membres")...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

II-D-3-a-ii - Vers un changement

La méthode change(int index) permet de naviguer vers le changement correspondant à l'index.

Navigation vers l'enregistrement d'index 2

assertThat(changes).change(2)...

Dans l'exemple ci-dessous, le changement sélectionné (en gras sur fond coloré) est le changement d'index 2.

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

La méthode change()row() (fonctionne comme , column() ou value()) permet de naviguer vers le changement suivant celui en cours ou le premier des changements s'il n'y a pas encore de navigation vers un changement.

Les méthodes change(int index) et change() sont les seules fonctionnant au niveau d'un sous-ensemble.

Les autres méthodes de navigation vers un changement décrites un peu plus loin dans le chapitre fonctionnent comme si elles étaient appelées depuis l'ensemble des changements même si elles sont appelées depuis un sous-ensemble.

Dans l'exemple ci-dessous, le changement sélectionné (en gras sur fond coloré) est le changement suivant celui d'index 1 parmi le sous-ensemble des changements sur la table MEMBRES.

Navigation vers le changement suivant celui d'index 1 parmi le sous-ensemble des changements sur la table MEMBRES

assertThat(changes).onTable("MEMBRES").change(1).change()...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Les méthodes suivantes reprennent les principes évoqués dans la navigation vers des sous-ensembles de changements :

- changeOfCreation(int index), changeOfModification(int index) et changeOfDeletion(int index) naviguent vers le changement de type CREATION, MODIFICATION et DELETION à l'index passé en paramètre ;

- changeOfCreation(), changeOfModification() et changeOfDeletion() naviguent vers le changement de type CREATION, MODIFICATION et DELETION suivant la précédente navigation ou le premier s'il n'y a pas de navigation précédente ;

- changeOfCreationOnTable(String tableName, int index), changeOfModificationOnTable(String tableName, int index) et changeOfDeletionOnTable(String tableName, int index) naviguent vers le changement de type CREATION, MODIFICATION et DELETION à l'index passé en paramètre sur la table donnée ;

- changeOfCreationOnTable(String tableName), changeOfModificationOnTable(String tableName) et changeOfDeletionOnTable(String tableName) naviguent vers le changement de type CREATION, MODIFICATION et DELETION sur la table donnée suivant la précédente navigation ou le premier s'il n'y a pas de navigation précédente .

Dans l'exemple ci-dessous, le changement sélectionné (en gras sur fond coloré) est le changement de création à l'index 1.

Navigation vers le changement de création à l'index 1

assertThat(changes).changeOfCreation(1)...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Pour finir sur la navigation vers un changement, la méthode changeOnTableWithPks(String tableName, Object… pksValues) permet de naviguer vers le changement d'une table donnée avec une clé primaire donnée.

Dans l'exemple ci-dessous, le changement sélectionné (en gras sur fond coloré) est le changement de la table APPARTENANCES avec la clé primaire [1, 7].

Navigation vers le changement de la table APPARTENANCES avec la clé primaire [1, 7]

assertThat(changes).changeOnTableWithPks("APPARTENANCES", 1, 7)...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

II-D-3-a-iii - Vers un enregistrement

La méthode rowAtStartPoint() permet de naviguer vers l'enregistrement au point de début.

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est l'enregistrement au point de début du changement d'index 2 parmi le sous-ensemble des changements sur la table MEMBRES.

Navigation vers l'enregistrement au point de début du changement d'index 2 parmi le sous-ensemble des changements sur la table MEMBRES

assertThat(changes).onTable("MEMBRES").change(2).rowAtStartPoint()...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

La méthode rowAtEndPoint() permet de naviguer vers l'enregistrement au point de début.

Dans l'exemple ci-dessous, l'enregistrement sélectionné (en gras sur fond coloré) est l'enregistrement au point de fin du changement d'index 2 parmi le sous-ensemble des changements sur la table MEMBRES.

Navigation vers l'enregistrement au point de fin du changement d'index 2 parmi le sous-ensemble des changements sur la table MEMBRES

assertThat(changes).onTable("MEMBRES").change(2).rowAtEndPoint()...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

II-D-3-a-iv - Vers une colonne

Les méthodes column(String columnName), column(int index) et column() fonctionnent de la même manière que cela a été décrit dans le chapitre II-D-2-ii.

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la colonne PRENOM du changement d'index 3.

Navigation vers la colonne PRENOM du changement d'index 3

assertThat(changes).change(3).column("prenom")...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Pour la navigation depuis un changement, il existe en plus les méthodes columnAmongTheModifiedOnes(String columnName), columnAmongTheModifiedOnes(int index) et columnAmongTheModifiedOnes() qui permettent de naviguer en ne tenant compte que des colonnes dont la valeur a été modifiée entre le point de départ et le point d'arrivée.

Pour les changements de type CREATION et DELETION, toutes les colonnes sont considérées comme avec une valeur modifiée. Donc ces méthodes n'ont réellement d'intérêt que pour les changements de type MODIFICATION.

La méthode columnAmongTheModifiedOnes() se comporte comme la méthode column(String columnName) (mis à part que columnAmongTheModifiedOnes() lève une exception si la colonne ne fait pas partie des colonnes avec la valeur modifiée).

Dans l'exemple ci-dessous, la colonne sélectionnée (en gras sur fond coloré) est la première colonne avec la valeur modifiée ou la colonne avec la valeur modifiée d'index 0.

Navigation vers la première colonne avec la valeur modifiée ou la colonne avec la valeur modifiée d'index 0

// Les deux lignes sont équivalentes
assertThat(changes).change(3).columnAmongTheModifiedOnes()...
assertThat(changes).change(3).columnAmongTheModifiedOnes(0)...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

II-D-3-a-v - Vers une valeur

Pour la navigation vers une valeur depuis un enregistrement, il existe les méthodes value(int index), value() et value(String columnName) qui fonctionnent comme décrit dans le chapitre II-D-2-a-iii.

Dans l'exemple ci-dessous, la valeur sélectionnée (en gras sur fond coloré) est la valeur de la colonne « PRENOM ».

Navigation vers la valeur de la colonne PRENOM

assertThat(changes).change(3).rowAtStartPoint().value("prenom")...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

Pour la navigation vers une valeur depuis une colonne, il existe les méthodes valueAtStartPoint() et valueAtEndPoint() qui permettent de naviguer vers la valeur au point de début ou celle au point de fin.

Dans l'exemple ci-dessous, la valeur sélectionnée (en gras sur fond coloré) est la valeur au point de début.

Navigation vers la valeur au point de début

assertThat(changes).change(3).column("prenom").valueAtStartPoint()...

Type du changement

Table du changement

Clé primaire de l'enregistrement changé

Changement

CREATION

APPARTENANCES

1, 8

ID_GROUPE

ID_MEMBRE

Au point de départ

Au point de fin

1

8

CREATION

MEMBRES

8

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

Au point de fin

8

Summers

Andrew James

31/12/1942

MODIFICATION

MEMBRES

5

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

Au point de fin

5

Sumner

Gordon Matthew Thomas

Sting

02/10/1951

1,82

DELETION

APPARTENANCES

1, 7

ID_GROUPE

ID_MEMBRE

Au point de départ

1

7

Au point de fin

DELETION

MEMBRES

7

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

Au point de départ

7

Padovani

Henry

13/10/1950

Au point de fin

II-D-3-b - La navigation « remontante »

Depuis un sous-ensemble de changements, il est possible de retourner vers l'ensemble des changements.
Depuis un changement (Change), il est possible de retourner vers des changements (Changes).
Depuis une colonne (Column) ou un enregistrement (Row) il est possible de retourner vers la Table ou la Request d'origine.
Et depuis une valeur, il est possible de retourner vers la colonne (Column) ou l'enregistrement (Row) d'origine.

II-D-3-b-i - Vers la colonne ou l'enregistrement

Si la valeur a été obtenue depuis un enregistrement, il est possible de retourner sur cet enregistrement avec la méthode returnToRow().

Retour vers l'enregistrement

assertThat(changes).change(3).rowAtStartPoint().value("prenom").returnToRow()...

Si la valeur a été obtenue depuis une colonne, il est possible de retourner sur cette colonne avec la méthode returnToColumn().

Retour vers la colonne

assertThat(changes).change(3).column("prenom").valueAtStartPoint().returnToColumn()...

II-D-3-b-ii - Vers le changement

Depuis une colonne ou un enregistrement, il est possible de retourner sur le changement avec la méthode returnToChange().

Retour vers le changement

assertThat(changes).change(3).rowAtStartPoint().returnToChange()...
assertThat(changes).change(3).column("prenom").returnToChange()...

II-D-3-b-iii - Vers les changements

Depuis un changement, il est possible de retourner sur les changements avec la méthode returnToChanges().

Retour vers les changements

assertThat(changes).change(3).returnToChanges()...

Depuis un sous-ensemble de changements, il est possible de retourner sur l'ensemble des changements avec la méthode returnToChanges().

Retour vers l'ensemble des changements

assertThat(changes).onTable("membres").ofAll()...

II-D-4 - Enchaînement de méthodes de navigation « descendante »

Nous avons vu qu'il était possible d'enchaîner des méthodes de navigation.

Navigation vers l'enregistrement suivant celui d'index 2

assertThat(table).row(2).row()...

Ce principe d'enchaînement est déclinable de pratiquement toutes les manières.
Il faut juste garder à l'esprit que les méthodes sont en réalité exécutées au niveau élément supérieur (ici la navigation de la méthode row() s'effectue l'élément table).
On peut en déduire que la ligne de code précédente est équivalente à celle-ci (c'est juste que le returnToTable() est implicite).

Navigation vers l'enregistrement suivant celui d'index 2

assertThat(table).row(2).returnToTable().row()...

Sur le même principe, nous pouvons enchaîner les méthodes suivantes :

Exemple d'enchaînement

assertThat(changes)
.ofDeletion()                     // vers le sous-ensemble des changements de type DELETION
.change()                     // Premier changement du sous-ensemble
.rowAtStartPoint()        // Enregistrement au point de départ de ce premier changement
.change()                     // Changement suivant
.column("prenom")         // Colonne PRENOM de ce changement
.valueAtStartPoint()  // Valeur au point de départ de cette colonne
.rowAtEndPoint()          // Enregistrement au point de fin de l'enregistrement
.changeOfModification()       // Premier changement de type MODIFICATION (un appel à .ofAll() juste avant est implicite)
.changeOfCreation(1)          // Changement de type CREATION à l'index 1
...

Si elle peut dériver vers un enchaînement absurde, cette facilité de navigation permet surtout une grande liberté au testeur. Il suffit comme nous le verrons un peu plus loin d'insérer les bonnes assertions entre les méthodes de navigation.

II-E - Les DateValue, TimeValue et DateTimeValue

AssertJ-DB en version 1.0 est compatible avec Java 7. Cela signifie qu'il n'est pas possible d'utiliser la nouvelle API date de Java 8 (LocalDate, LocalTime et LocalDateTime).
Ces classes (bien que moins puissantes que celles de Java 8) fournissent une solution simple pour comparer des valeurs de la base de données avec des dates, heures et dates/heures.

Ces classes sont instanciables par trois méthodes statiques of(...) (probablement la méthode la plus intéressante pour les testeurs), from(...) (qui reçoit l'équivalence du package java.sql : java.sql.Date, java.sql.Time et java.sql.Timestamp) et parse(...) (qui reçoit une chaîne de caractères représentant la valeur, mais qui peut lever une exception ParseException).

Il est possible de consulter plus d'informations dans la documentation officielle.

II-E-1 - Les DateValue

Un objet DateValue représente une date. Il peut être instancié en utilisant les méthodes :

- of(...) :

Instanciation avec des int en paramètres

DateValue dateValue = DateValue.of(2007, 12, 23);

- from(...) :

Instanciation avec un java.sql.Date en paramètre

Date date = Date.valueOf("2007-12-23");
DateValue dateValue = DateValue.from(date);

- parse(...) :

Instanciation avec un String en paramètre

DateValue dateValue = DateValue.parse("2007-12-23");

II-E-2 - Les TimeValue

Un objet TimeValue représente une heure. Il peut être instancié en utilisant les méthodes :

- of(...) :

Instanciation avec des int en paramètres

// En indiquant juste les heures et minutes
TimeValue timeValue1 = TimeValue.of(9, 1);
// Avec les secondes en plus
TimeValue timeValue2 = TimeValue.of(9, 1, 6);
// Avec les nanosecondes en plus
TimeValue timeValue3 = TimeValue.of(9, 1, 6, 3);

- from(...) :

Instanciation avec un java.sql.Time en paramètre

Time time = Time.valueOf("09:01:06");
TimeValue timeValue = TimeValue.from(time);

- parse(...) :

Instanciation avec un String en paramètre

// En indiquant juste les heures et minutes
TimeValue timeValue1 = TimeValue.parse("09:01");
// Avec les secondes en plus
TimeValue timeValue2 = TimeValue.parse("09:01:06");
// Avec les nanosecondes en plus
TimeValue timeValue3 = TimeValue.parse("09:01:06.000000003");

II-E-3 - Les DateTimeValue

Un objet DateTimeValue représente une date/heure. Il peut être instancié en utilisant les méthodes :

- of(...) :

Instanciation avec un DateValue et un TimeValue en paramètres

// Avec la date seulement (l'heure est mise à minuit)
DateTimeValue dateTimeValue1 = DateTimeValue.of(dateValue);
// Avec la date et l'heure
DateTimeValue dateTimeValue2 = DateTimeValue.of(dateValue, timeValue1);

- from(...) :

Instanciation avec un java.sql.Timestamp en paramètre

Timestamp timestamp = Timestamp.valueOf("2007-12-23 09:01:06.000000003");
DateTimeValue dateTimeValue = DateTimeValue.from(timestamp);

- parse(...) :

Instanciation avec un String en paramètre

// Avec la date seulement (l'heure est mise à minuit)
DateTimeValue dateTimeValue1 = DateTimeValue.parse("2007-12-23");
// Avec la date et l'heure (en indiquant juste les heures et minutes)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01");
// Avec la date et l'heure (avec les secondes en plus)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01:06");
// Avec la date et l'heure (avec les nanosecondes en plus)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01:06.000000003");

II-F - Les descriptions par défaut

Les descriptions sont un concept déjà existant dans AssertJ. Elles se rajoutent avec les méthodes de l'interface Descriptable. La description est utilisée dans le message d'erreur qui apparaît lorsqu'un test échoue.

Dans AssertJ-DB, à cause de la navigation, il est plus compliqué d'être certain de quel élément provient l'erreur. Donc afin d'aider le testeur, AssertJ-DB comporte des descriptions par défaut.

Voici quelques exemples de descriptions par défaut :

- « members table » pour une assertion sur une table ;

- « 'select * from actor' request » pour une assertion sur une requête SQL ;

- « 'select id, name, firstname, bi...' request » pour une assertion sur une requête SQL donc le code est plus long ;

- « Row at index 0 of members table » pour une assertion sur un enregistrement d'une table ;

- « Column at index 0 (column name : ID) of 'select * from members' request » pour une assertion sur une colonne d'une requête SQL ;

- « Value at index 0 of Column at index 0 (column name : ID) of 'select * from members' request » pour une assertion sur une valeur d'une colonne d'une requête SQL ;

- « Value at index 0 (column name : ID) of Row at index 0 of 'select * from members' request » pour une assertion sur une valeur d'un enregistrement d'une requête SQL ;

- « Value at index 0 (column name : ID) of Row at end point of Change at index 0 (on table : MEMBERS and with primary key : [4]) of Changes on tables of 'sa/jdbc:h2:mem:test' source » pour une assertion sur une valeur de l'enregistrement au point de fin d'un changement sur une table.

Ces descriptions par défaut peuvent tout à fait être remplacées par le choix du testeur en faisant appel aux méthodes classiques de l'interface Descriptable.

On peut voir (notamment pour le dernier exemple) que le texte d'une description par défaut peut devenir long. En effet, le texte se cumule au fur et à mesure de la navigation « descendante » (voir entre le premier exemple et le quatrième le rajout de « Row at index 0 of »). Cela signifie que si la description d'un élément est modifiée par le testeur avec les méthodes de l'interface Descriptable, toutes les descriptions par défaut dépendantes seront modifiées également.

Il est possible de consulter plus d'informations dans la documentation officielle.

III - Exemples d'assertion

Les assertions possibles sont les mêmes pour les tables et les requêtes. Généralement, les exemples présenteront chaque assertion à partir d'une table et à partir d'une requête. Quelques rares cas ne présenteront que pour un des deux, mais c'est uniquement à cause du jeu de données de l'exemple. En réalité si une assertion existe pour une table, elle existe pour une requête et inversement.

Pour les exemples, ci-dessous, d'assertion sur des tables ou requêtes, l'état des tables sera comme ceci :

Table GROUPES

ID

NOM

1

The Police

2

U2

Table MEMBRES

ID

NOM

PRENOM

SURNOM

DATE_NAISSANCE

TAILLE

1

Hewson

Paul David

Bono

10/05/1960

1,75

2

Evans

David Howell

The Edge

08/08/1961

1,77

3

Clayton

Adam

13/03/1960

1,78

4

Mullen

Larry

31/10/1961

1,70

5

Sumner

Gordon Matthew Thomas

02/10/1951

1,82

6

Copeland

Stewart Armstrong

16/07/1952

1,87

7

Padovani

Henry

13/10/1950

Table APPARTENANCES

ID_GROUPE

ID_MEMBRE

1

5

1

6

1

7

2

1

2

2

2

3

2

4

Pour les exemples d'assertion sur des changements, les requêtes juste en dessous sont exécutées entre le point de départ et le point de fin.

Requêtes SQL des modifications entre le point de début et le point de fin

delete from appartenances where id_membre = 7;
delete from membres where id = 7;
insert into membres(id, nom, prenom, date_naissance) values(8, 'Summers', 'Andrew James', PARSEDATETIME('31/12/1942', 'dd/MM/yyyy'));
insert into appartenances values(1, 8);
update membres set surnom = 'Sting' where id = 5;

Les exemples d'assertion vont être écrits dans des classes de tests présentées juste en dessous : AbstractTest est la classe parente des classes TestChanges pour les exemples sur les changements, TestTable pour les exemples sur les tables et TestRequest pour les exemples sur les requêtes.

La classe AbstractTest ci-dessous permet d'initialiser la base de données avant les tests :

AbstractTest.java

package com.developpez.rpouiller.assertj.db;

import static com.ninja_squad.dbsetup.Operations.deleteAllFrom;
import static com.ninja_squad.dbsetup.Operations.insertInto;
import static com.ninja_squad.dbsetup.Operations.sequenceOf;

import java.sql.Connection;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Statement;

import org.assertj.db.type.Request;
import org.assertj.db.type.Table;
import org.h2.jdbcx.JdbcConnectionPool;
import org.junit.Before;
import org.junit.BeforeClass;

import com.ninja_squad.dbsetup.DbSetup;
import com.ninja_squad.dbsetup.DbSetupTracker;
import com.ninja_squad.dbsetup.destination.DataSourceDestination;
import com.ninja_squad.dbsetup.operation.Operation;

/**
* Classe parente de tous les tests.
*/
public class AbstractTest {

public static JdbcConnectionPool dataSource;
public static DbSetupTracker dbSetupTracker = new DbSetupTracker();

protected Table tableMembres = new Table(dataSource, "MEMBRES");
protected Table tableMembresSansSurnom = new Table(dataSource,
"MEMBRES",
null,
new String[] { "SURNOM" });
protected Request requestWithPks = new Request(dataSource,
"select groupes.nom nom_groupe, " +
"       membres.nom nom_membre, " +
"       membres.surnom, " +
"       membres.id id_membre " +
" from membres, " +
"      groupes, " +
"      appartenances " +
" where membres.id = appartenances.id_membre " +
" and groupes.id = appartenances.id_groupe " +
" and membres.taille is not null").setPksName("id_membre");;
protected Request requestWithoutPks = new Request(dataSource,
"select groupes.nom nom_groupe, " +
"       membres.nom nom_membre, " +
"       membres.surnom, " +
"       membres.id id_membre " +
" from membres, " +
"      groupes, " +
"      appartenances " +
" where membres.id = appartenances.id_membre " +
" and groupes.id = appartenances.id_groupe " +
" and membres.taille is not null");

/**
* Création de la source de données (globale à la classe).
* Utilisation de JDBC pour cette création.
* @throws SQLException
*/
@BeforeClass
public static void setUpGlobal() throws SQLException {
if (dataSource == null) {
dataSource = JdbcConnectionPool.create("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "user", "password");
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("create table membres("
+ "id number primary key, "
+ "nom varchar not null, "
+ "prenom varchar not null, "
+ "surnom varchar, "
+ "date_naissance date, "
+ "taille decimal);");
statement.executeUpdate("create table groupes("
+ "id number primary key, "
+ "nom varchar not null);");
statement.executeUpdate("create table appartenances("
+ "id_groupe number, "
+ "id_membre number, "
+ "primary key(id_groupe, id_membre), "
+ "foreign key(id_groupe) references groupes(id), "
+ "foreign key(id_membre) references membres(id));");
}
}
}
}

/**
* Chargement des données en base (avant chaque test).
* Utilisation de DBSetup (effacement de toutes les données puis insertion).
*/
@Before
public void setUp() throws Exception {
Operation OperationInsert1 = insertInto("membres")
.columns("id", "nom", "prenom", "surnom", "date_naissance", "taille")
.values(1, "Hewson", "Paul David", "Bono", Date.valueOf("1960-05-10"), 1.75)
.values(2, "Evans", "David Howell", "The Edge", Date.valueOf("1961-08-08"), 1.77)
.values(3, "Clayton", "Adam", null, Date.valueOf("1960-03-13"), 1.78)
.values(4, "Mullen", "Larry", null, Date.valueOf("1961-10-31"), 1.7)
.values(5, "Sumner", "Gordon Matthew Thomas", null, Date.valueOf("1951-10-02"), 1.82)
.values(6, "Copeland", "Stewart Armstrong", null, Date.valueOf("1952-07-16"), 1.87)
.values(7, "Padovani", "Henry", null, Date.valueOf("1950-10-13"), null)
.build();
Operation OperationInsert2 = insertInto("groupes")
.columns("id", "nom")
.values(1, "The Police")
.values(2, "U2")
.build();
Operation OperationInsert3 = insertInto("appartenances")
.columns("id_groupe", "id_membre")
.values(1, 5)
.values(1, 6)
.values(1, 7)
.values(2, 1)
.values(2, 2)
.values(2, 3)
.values(2, 4)
.build();

DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource),
sequenceOf(deleteAllFrom("appartenances", "groupes", "membres"),
OperationInsert1, OperationInsert2, OperationInsert3));

dbSetupTracker.launchIfNecessary(dbSetup);
}
}

On remarque les champs tableMembres, tableMembresSansSurnom, requestWithPks et requestWithoutPks qui seront utilisés dans les tests :

- tableMembres correspond à un objet Table sur la table MEMBRES de la base de données ;

- tableMembresSansSurnom correspond à la même chose sauf que la colonne SURNOM est exclue ;

- requestWithPks correspond à un objet Request sur lequel on a précisé la clé primaire ;

- requestWithoutPks correspond à la même chose sauf que la clé primaire n'a pas été précisée ;

Pour le chargement des données avant le test grâce à DBSetup, dbSetupTracker permet avec la méthode launchIfNecessary de ne pas recharger les données si le test précédent a indiqué que ce n'était pas nécessaire avec la méthode skipNextLaunch comme cela sera visible dans les méthodes des tests sur des tables ou des requêtes.

TestChanges.java

package com.developpez.rpouiller.assertj.db;

import static org.assertj.db.api.Assertions.assertThat;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import org.assertj.db.type.Changes;
import org.junit.Before;
import org.junit.Test;

/**
* Test sur des changements.
*/
public class TestChanges extends AbstractTest {

private Changes changesTableMembres = new Changes(tableMembres);
private Changes changesTableMembresSansSurnom = new Changes(tableMembresSansSurnom);
private Changes changesRequestWithoutPks  = new Changes(requestWithoutPks);
private Changes changesRequestWithPks = new Changes(requestWithPks);
private Changes changesDataSource = new Changes(dataSource);

private void faireChangements() throws SQLException {
try (Connection connection = dataSource.getConnection()) {
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("delete from appartenances where id_membre = 7;");
statement.executeUpdate("delete from membres where id = 7;");
statement.executeUpdate("insert into membres(id, nom, prenom, date_naissance) values(8, 'Summers', 'Andrew James', PARSEDATETIME('31/12/1942', 'dd/MM/yyyy'));");
statement.executeUpdate("insert into appartenances values(1, 8);");
statement.executeUpdate("update membres set surnom = 'Sting' where id = 5;");
}
}
}

/**
* Modification des données.
*/
@Before
public void setUpChanges() throws Exception {
changesTableMembres.setStartPointNow();
changesTableMembresSansSurnom.setStartPointNow();
changesRequestWithoutPks.setStartPointNow();
changesRequestWithPks.setStartPointNow();
changesDataSource.setStartPointNow();
faireChangements();
changesTableMembres.setEndPointNow();
changesTableMembresSansSurnom.setEndPointNow();
changesRequestWithoutPks.setEndPointNow();
changesRequestWithPks.setEndPointNow();
changesDataSource.setEndPointNow();
}
}

La classe de tests sur les changements initialise les objets Changes :

- changesTableMembres pour les changements sur la table MEMBRES ;

- changesTableMembresSansSurnom pour les changements sur la table MEMBRES sans tenir compte de la colonne SURNOM ;

- changesRequestWithoutPks pour les changements sur la requête sans clé primaire ;

- changesRequestWithPks pour les changements sur la requête avec clé primaire ;

- changesDataSource pour les changements sur l'ensemble de la base de données.

Ces objets Changes sont initialisés avant chaque test juste après le chargement des données dans la base.

TestTable.java

package com.developpez.rpouiller.assertj.db;

import static org.assertj.db.api.Assertions.assertThat;

import org.junit.After;
import org.junit.Test;

/**
* Tests sur une table.
*/
public class TestTable extends AbstractTest {

/**
* Pas besoin de recharger car pas de modification
* des données lors des tests
*/
@After
public void skip() {
dbSetupTracker.skipNextLaunch();
}
}

TestRequest.java

package com.developpez.rpouiller.assertj.db;

import static org.assertj.db.api.Assertions.assertThat;

import org.junit.After;
import org.junit.Test;

/**
* Tests sur une requête.
*/
public class TestRequest extends AbstractTest {

/**
* Pas besoin de recharger car pas de modification
* des données lors des tests
*/
@After
public void skip() {
dbSetupTracker.skipNextLaunch();
}
}

Pour les tests sur les tables et requêtes, on indique après chaque test qu'il n'est pas nécessaire de recharger les données avec dbSetupTracker.skipNextLaunch();.

III-A - Assertion sur les nombres de changements, colonnes et enregistrements

III-A-1 - Assertion sur les nombres de changements

La méthode hasNumberOfChanges(int) permet de vérifier que le nombre de changements correspond au paramètre. On remarque que tous les objets Changes ne contiennent pas le même nombre de changements (voir le chapitre III-B-3).

TestChanges.java

@Test
public void testNumberOfChanges() {
// La table MEMBRES comporte 3 changements
assertThat(changesTableMembres).hasNumberOfChanges(3);
// Si on ignore la colonne SURNOM, cela ne laisse que 2 changements
assertThat(changesTableMembresSansSurnom).hasNumberOfChanges(2);
// Sans PK identifié, la modification est considérée comme un effacement et un création
assertThat(changesRequestWithoutPks).hasNumberOfChanges(2);
// Avec un PK identifié, la modification est bien reconnue
assertThat(changesRequestWithPks).hasNumberOfChanges(1);
// Au total, la base comporte 5 changements
assertThat(changesDataSource).hasNumberOfChanges(5);
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-A-2 - Assertion sur les nombres de colonnes

La méthode hasNumberOfColumns(int) permet de vérifier que le nombre de colonnes correspond au paramètre. Il est possible de vérifier le nombre de colonnes au niveau d'un changement (Change) ou de l'élément racine (Table ou Request), mais également au niveau d'un enregistrement. On peut voir dans les exemples ci-dessous que les assertions et la navigation peuvent s'enchaîner.

TestChanges.java

@Test
public void testNumberOfColumns() {
assertThat(changesTableMembresSansSurnom).change().hasNumberOfColumns(5)
.rowAtEndPoint().hasNumberOfColumns(5); // Les 5 colonnes de la table sans la colonne exclue
}

TestTable.java

@Test
public void testNumberOfColumns() {
assertThat(tableMembres).hasNumberOfColumns(6)
.row().hasNumberOfColumns(6);
}

TestRequest.java

@Test
public void testNumberOfColumns() {
assertThat(requestWithPks).hasNumberOfColumns(4)
.row().hasNumberOfColumns(4);
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-A-3 - Assertion sur les nombres d'enregistrements

La méthode hasNumberOfRows(int) permet de vérifier que le nombre de colonnes correspond au paramètre. Il est possible de vérifier le nombre d'enregistrements à l'élément racine (Table ou Request), mais également au niveau d'une colonne.

TestTable.java

@Test
public void testNumberOfRows() {
assertThat(tableMembresSansSurnom).hasNumberOfRows(7)
.column().hasNumberOfRows(7);
}

TestRequest.java

@Test
public void testNumberOfRows() {
assertThat(requestWithoutPks).hasNumberOfRows(6)
.column().hasNumberOfRows(6);
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-B - Assertion sur les changements

III-B-1 - Assertion sur le type de changement

La méthode isOfType(ChangeType) (voir le concept de type de changement au chapitre III-C-2) permet de vérifier que le type de changement correspond au paramètre.
Cette méthode est complétée par les méthodes isCreation(), isModification() et isDeletion() qui permettent également de comparer le type du changement avec celui correspondant à la méthode.

TestChanges.java

@Test
public void testTypeOfChanges() {
// 2 premiers sont des créations, le suivant une modification et le dernier une suppression
assertThat(changesDataSource).hasNumberOfChanges(5)
.change().isOfType(ChangeType.CREATION)
.change().isCreation()
.change().isModification().isOfType(ChangeType.MODIFICATION)
.change(4).isOfType(ChangeType.DELETION).isDeletion();
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-B-2 - Assertion sur le type de données

La méthode isOnDataType(DataType) (voir le concept de type de données au chapitre III-C-1) permet de vérifier que le type de données correspond au paramètre.
Cette méthode est complétée par les méthodes isOnRequest() et isOnTable() qui permettent également de comparer le type du données avec celui correspondant à la méthode.
La méthode isOnTable(String) contrôle que le changement est sur la table dont le nom est indiqué en paramètre.

TestChanges.java

@Test
public void testTypeOfData() {
assertThat(changesRequestWithoutPks).hasNumberOfChanges(2)
.change().isCreation()
.isOnDataType(DataType.REQUEST)
.isOnRequest();
assertThat(changesDataSource).hasNumberOfChanges(5)
.change().isCreation()
.isOnDataType(DataType.TABLE)
.change().isCreation()
.isOnTable()
.change().isModification()  // Modification dans MEMBRES
.isOnTable("MEMBRES")
.ofDeletion().hasNumberOfChanges(2)
.change().isOnTable("appartenances")  // Suppression dans APPARTENANCES
.change().isOnTable("membres");       // Suppression dans MEMBRES
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-B-3 - Assertion sur les clés primaires

La méthode hasPksNames(String...) permet de vérifier que le nom des colonnes composant la clé primaire d'un changement.
La méthode hasPksValues(String...) permet de vérifier que la valeur des colonnes composant la clé primaire d'un changement.

TestChanges.java

@Test
public void testPrimaryKeys() {
assertThat(changesRequestWithPks).hasNumberOfChanges(1)
.change().isModification()
.hasPksNames("ID_MEMBRE")    // ID_MEMBRE a été fixé comme PK
.hasPksValues(5);
assertThat(changesDataSource).hasNumberOfChanges(5)
.change().isOnTable("APPARTENANCES")
.hasPksNames("ID_GROUPE", "ID_MEMBRE")
.hasPksValues(1, 8)    // PK de l'enregistrement créé
.change().isOnTable("MEMBRES")
.hasPksNames("ID")
.hasPksValues(8);      // PK de l'enregistrement créé
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-B-4 - Assertion sur l'existence d'un enregistrement

La méthode exists() permet de vérifier que l'enregistrement existe.
La méthode doesNotExist() permet de vérifier que l'enregistrement n'existe pas (par exemple, l'enregistrement au point de début pour un changement de création).

TestChanges.java

@Test
public void testExistence() {
assertThat(changesDataSource)
.changeOfCreation()   // L'enregistrement n'existe pas encore au point de départ
.rowAtStartPoint().doesNotExist()
.rowAtEndPoint().exists()
.changeOfModification()
.rowAtStartPoint().exists()
.rowAtEndPoint().exists()
.changeOfDeletion()   // L'enregistrement n'existe plus au point de fin
.rowAtStartPoint().exists()
.rowAtEndPoint().doesNotExist();
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-B-5 - Assertion sur les colonnes modifiées

La méthode hasNumberOfModifiedColumns(int) permet de vérifier que le nombre de colonnes modifiées correspond au paramètre.
La méthode hasModifiedColumns(String...) permet de vérifier que les colonnes modifiées correspondent aux noms de colonnes en paramètres.
La méthode hasModifiedColumns(int...) permet de vérifier que les colonnes modifiées correspondent aux index de colonnes en paramètres.
La méthode isModified() permet de vérifier que la colonne est modifiée.
La méthode isNotModified() permet de vérifier que la colonne n'est pas modifiée.

TestChanges.java

@Test
public void testModifiedColumns() {
assertThat(changesDataSource)   // Seule la colonne "surnom" à l'index 3 est modifiée
.changeOfModification().hasNumberOfModifiedColumns(1)
.hasModifiedColumns("surnom")
.hasModifiedColumns(3)
.column("surnom").isModified()
.column().isNotModified();
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C - Assertion sur les valeurs

III-C-1 - Assertion sur le type de valeur

La méthode isOfType(ValueType) (voir le concept de type de valeur au chapitre III-C-3) permet de vérifier que le type de la valeur correspond au type de valeur en paramètre.
Cette méthode est complétée par les méthodes isBoolean(), isBytes(), isDate(), isDateTime(), isNumber(), isText() et isTime() qui permettent également de comparer le type de la valeur avec celui correspondant à la méthode.
La méthode isOfAnyTypeIn(ValueType...) permet de vérifier que le type de la valeur correspond à un des types de valeur en paramètres.

TestChanges.java

@Test
public void testTypeOfValue() {
assertThat(changesTableMembres)
.change(1)
.rowAtEndPoint()
.value().isOfType(ValueType.NUMBER) // ID est une valeur numérique
.value().isOfType(ValueType.TEXT)   // NOM est une valeur texte
.value().isText()                   // PRENOM est une valeur texte
.value().isOfType(ValueType.TEXT)   // SURNOM est une valeur texte
.isText()
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE)
.value().isDate()                   // DATE_NAISSANCE est une valeur date
.value().isNumber();                // TAILLE est une valeur numérique
}

TestTable.java

@Test
public void testTypeOfValue() {
assertThat(tableMembres)
.row()
.value().isOfType(ValueType.NUMBER) // ID est une valeur numérique
.value().isOfType(ValueType.TEXT)   // NOM est une valeur texte
.value().isText()                   // PRENOM est une valeur texte
.value().isOfType(ValueType.TEXT)   // SURNOM est une valeur texte
.isText()
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE)
.value().isDate()                   // DATE_NAISSANCE est une valeur date
.value().isNumber();                // TAILLE est une valeur numérique
}

TestRequest.java

@Test
public void testTypeOfValue() {
assertThat(requestWithPks)
.row()
.value().isOfType(ValueType.TEXT)   // NOM_GROUPE est une valeur texte
.value().isText()                   // NOM_MEMBRE est une valeur texte
.value().isOfType(ValueType.TEXT)   // SURNOM est une valeur texte
.isText()
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE)
.value().isNumber();                // ID_MEMBRE est une valeur numérique
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C-2 - Assertion sur la nullité d'une valeur

La méthode isNull() permet de vérifier que la valeur est NULL.
La méthode isNotNull() permet de vérifier que la valeur est non NULL.

TestChanges.java

@Test
public void testNullite() {
assertThat(changesTableMembres)
.change(1)
.column("surnom")     // La valeur passe de NULL à non NULL
.valueAtStartPoint().isNull()
.valueAtEndPoint().isNotNull();
}

TestTable.java

@Test
public void testNullite() {
assertThat(tableMembres)
.column("surnom")   // Les deux premières valeurs sont non NULL
.value().isNotNull()
.value().isNotNull()
.value().isNull()
.value().isNull();
}

TestRequest.java

@Test
public void testNullite() {
assertThat(requestWithPks)
.column("surnom")   // Les deux premières valeurs sont non NULL
.value().isNotNull()
.value().isNotNull()
.value().isNull()
.value().isNull();
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C-3 - Assertion sur l'égalité d'une valeur

La méthode isEqualTo(...) permet de vérifier que la valeur est égale au paramètre.
Le paramètre doit correspondre au type de valeur (voir le concept de type de valeur au chapitre III-C-3)  : par exemple un objet DateValue en paramètre sera pour une valeur de type DATE. Cependant, comme c'est visible ci-dessous, un paramètre String peut être utilisé sur presque tous les types de valeur (AssertJ-DB convertit la chaîne de caractères dans un objet compatible).

TestChanges.java

@Test
public void testEquality() throws ParseException {
assertThat(changesTableMembres)
.ofModification().change().rowAtEndPoint()
.value().isEqualTo(5)
.isEqualTo("5")
.value().isEqualTo("Sumner")
.value().isEqualTo("Gordon Matthew Thomas")
.value().isEqualTo("Sting")
.value().isEqualTo(DateTimeValue.parse("1951-10-02"))
.isEqualTo(DateValue.of(1951, 10, 2))
.isEqualTo("1951-10-02")
.value().isEqualTo(1.82)
.isEqualTo("1.82");
}

TestTable.java

@Test
public void testEquality() throws ParseException {
assertThat(tableMembres)
.row()
.value().isEqualTo(1)
.isEqualTo("1")
.value().isEqualTo("Hewson")
.value().isEqualTo("Paul David")
.value().isEqualTo("Bono")
.value().isEqualTo(DateTimeValue.parse("1960-05-10"))
.isEqualTo(DateValue.of(1960, 5, 10))
.isEqualTo("1960-05-10")
.value().isEqualTo(1.75)
.isEqualTo("1.75");
}

TestRequest.java

@Test
public void testEquality() throws ParseException {
assertThat(requestWithPks)
.row()
.value().isEqualTo("U2")
.value().isEqualTo("Hewson")
.value().isEqualTo("Bono")
.value().isEqualTo(1)
.isEqualTo("1");
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C-4 - Assertion sur la non-égalité d'une valeur

La méthode isNotEqualTo(...) permet de vérifier l'exact contraire de la méthode isEqualTo(...) (que la valeur n'est pas égale au paramètre).

TestChanges.java

@Test
public void testNoEquality() throws ParseException {
assertThat(changesTableMembres)
.ofModification().change().rowAtEndPoint()
.value().isNotEqualTo(6)
.isNotEqualTo("7")
.value().isNotEqualTo("Sumnerr")
.value().isNotEqualTo("Gordon Matt Thomas")
.value().isNotEqualTo("Stingg")
.value().isNotEqualTo(DateTimeValue.parse("1950-10-02"))
.isNotEqualTo(DateValue.of(1951, 11, 2))
.isNotEqualTo("1951-10-03")
.value().isNotEqualTo(1.83)
.isNotEqualTo("2");
}

TestTable.java

@Test
public void testNoEquality() throws ParseException {
assertThat(tableMembres)
.row()
.value().isNotEqualTo(2)
.isNotEqualTo("3")
.value().isNotEqualTo("Hewsonn")
.value().isNotEqualTo("Paul")
.value().isNotEqualTo("Bono Vox")
.value().isNotEqualTo(DateTimeValue.parse("1961-05-10"))
.isNotEqualTo(DateValue.of(1960, 6, 10))
.isNotEqualTo("1960-05-11")
.value().isNotEqualTo(1.76)
.isNotEqualTo("0.75");
}

TestRequest.java

@Test
public void testNoEquality() throws ParseException {
assertThat(requestWithPks)
.row()
.value().isNotEqualTo("U3")
.value().isNotEqualTo("Hexson")
.value().isNotEqualTo("Bon")
.value().isNotEqualTo(2)
.isNotEqualTo("3");
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C-5 - Assertion sur la chronologie d'une valeur

La méthode isBefore(...) permet de vérifier que la valeur est antérieure au paramètre.
La méthode isAfter(...) permet de vérifier que la valeur est postérieure au paramètre.
La méthode isBeforeOrEqualTo(...) permet de vérifier que la valeur est antérieure ou égale au paramètre.
La méthode isAfterOrEqualTo(...) permet de vérifier que la valeur est postérieure ou égale au paramètre.
Ces assertions permettent de comparer des dates, des heures et des dates/heures. Le paramètre peut également être une chaîne de caractères, dans ce cas le paramètre est converti selon la valeur en date, heure ou date/heure.

TestChanges.java

@Test
public void testChronology() throws ParseException {
assertThat(changesTableMembres)
.change().rowAtEndPoint()
.value("date_naissance")
.isBefore(DateValue.of(1943, 1, 1))
.isBefore("1943-01-01")
.isBeforeOrEqualTo(DateValue.of(1942, 12, 31))
.isBeforeOrEqualTo("1942-12-31")
.isAfterOrEqualTo(DateValue.of(1942, 12, 31))
.isAfterOrEqualTo("1942-12-31")
.isAfter(DateValue.of(1942, 12, 30))
.isAfter("1942-12-30");
}

TestTable.java

@Test
public void testChronology() throws ParseException {
assertThat(tableMembres)
.row()
.value("date_naissance")
.isBefore(DateValue.of(1960, 5, 11))
.isBefore("1960-05-11")
.isBeforeOrEqualTo(DateValue.of(1960, 5, 10))
.isBeforeOrEqualTo("1960-05-10")
.isAfterOrEqualTo(DateValue.of(1960, 5, 10))
.isAfterOrEqualTo("1960-05-10")
.isAfter(DateValue.of(1960, 05, 9))
.isAfter("1960-05-09");
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-C-6 - Assertion sur la comparaison d'une valeur

La méthode isGreaterThan(...) permet de vérifier que la valeur est supérieure au paramètre.
La méthode isLessThan(...) permet de vérifier que la valeur est inférieure au paramètre.
La méthode isGreaterThanOrEqualTo(...) permet de vérifier que la valeur est supérieure ou égale au paramètre.
La méthode isLessThanOrEqualTo(...) permet de vérifier que la valeur est inférieure ou égale au paramètre.

TestChanges.java

@Test
public void testComparison() throws ParseException {
assertThat(changesTableMembres)
.changeOfModification().rowAtEndPoint()
.value("id")
.isGreaterThan(4)
.isLessThan(6)
.isGreaterThanOrEqualTo(5)
.isLessThanOrEqualTo(5)
.value("taille")
.isGreaterThan(1.81)
.isLessThan(1.83)
.isGreaterThanOrEqualTo(1.82)
.isLessThanOrEqualTo(1.82);
}

TestTable.java

@Test
public void testComparison() throws ParseException {
assertThat(tableMembres)
.row()
.value("id")
.isGreaterThan(0)
.isLessThan(2)
.isGreaterThanOrEqualTo(1)
.isLessThanOrEqualTo(1)
.value("taille")
.isGreaterThan(1.74)
.isLessThan(1.76)
.isGreaterThanOrEqualTo(1.75)
.isLessThanOrEqualTo(1.75);
}

TestRequest.java

@Test
public void testComparison() throws ParseException {
assertThat(requestWithPks)
.row()
.value("id_membre")
.isGreaterThan(0)
.isLessThan(2)
.isGreaterThanOrEqualTo(1)
.isLessThanOrEqualTo(1);
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-D - Assertion sur l'égalité avec les valeurs d'un enregistrement

La méthode hasValues(Object...) permet de vérifier que les valeurs contenues dans un enregistrement sont égales à celles en paramètre.
Comme pour la méthode isEqualTo(...), il est possible d'utiliser des chaînes de caractères pour comparer les valeurs (même pour des valeurs qui ne sont pas de type TEXT).

TestChanges.java

@Test
public void testRowEquality() throws ParseException {
assertThat(changesTableMembres)
.changeOfModification()
.rowAtStartPoint()
.hasValues(5,
"Sumner",
"Gordon Matthew Thomas",
null,
DateValue.of(1951, 10, 2),
1.82)
.rowAtEndPoint()
.hasValues(5,
"Sumner",
"Gordon Matthew Thomas",
"Sting",
DateValue.of(1951, 10, 2),
1.82);
}

TestTable.java

@Test
public void testRowEquality() throws ParseException {
assertThat(tableMembres)
.row()
.hasValues(1,
"Hewson",
"Paul David",
"Bono",
DateValue.of(1960, 5, 10),
1.75)
.hasValues("1",
"Hewson",
"Paul David",
"Bono",
"1960-05-10",
"1.75");
}

TestRequest.java

@Test
public void testRowEquality() throws ParseException {
assertThat(requestWithPks)
.row()
.hasValues("U2",
"Hewson",
"Bono",
1);
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-E - Assertion sur les colonnes

III-E-1 - Assertion sur le nom des colonnes

La méthode hasColumnName(String) permet de vérifier que le nom de la colonne est égal au paramètre.

TestChanges.java

@Test
public void testColumnName() throws ParseException {
assertThat(changesDataSource).onTable("apparTenances")
.change()
.column().hasColumnName("id_groupe")
.column().hasColumnName("id_Membre");
}

TestTable.java

@Test
public void testColumnName() throws ParseException {
assertThat(tableMembres)
.column("nom").hasColumnName("NOM")
.column().hasColumnName("prenom")
.column(4).hasColumnName("Date_Naissance")
.column().hasColumnName("TAILLE");
}

TestRequest.java

@Test
public void testColumnName() throws ParseException {
assertThat(requestWithPks)
.column().hasColumnName("NOM_groupe")
.column(2).hasColumnName("suRnom")
.column().hasColumnName("id_Membre");
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-E-2 - Assertion sur le type de valeur d'une colonne

La méthode isOfType(ValueType, boolean) (voir le concept de type de valeur au chapitre III-C-3) permet de vérifier que le type de la valeur de la colonne correspond au type de valeur en paramètre.
Cette méthode est complétée par les méthodes isBoolean(boolean), isBytes(boolean), isDate(boolean), isDateTime(boolean), isNumber(boolean), isText(boolean) et isTime(boolean) qui permettent également de comparer le type de la valeur avec celui correspondant à la méthode.
Le paramètre boolean des méthodes permet de définir si le contrôle est tolérant ou pas : un contrôle non tolérant considère que la colonne ne doit contenir aucune la valeur null (null est NOT_IDENTIFIED) tandis qu'un contrôle tolérant considère que la colonne peut contenir des valeurs null.
La méthode isOfAnyTypeIn(ValueType...) permet de vérifier que le type de la valeur correspond à un des types de valeur en paramètres.

TestChanges.java

@Test
public void testTypeOfValuesInColumn() {
assertThat(changesTableMembres)
.change(1).isModification()
.column().isOfType(ValueType.NUMBER, false) // ID est une valeur numérique
.column().isOfType(ValueType.TEXT, false)   // NOM est une valeur texte
.column().isText(false)                     // PRENOM est une valeur texte
.column().isOfType(ValueType.TEXT, true)    // SURNOM est une valeur texte
.isText(true)                      // Mais la valeur est null au point de départ
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE, ValueType.NOT_IDENTIFIED)
.column().isDate(false)                     // DATE_NAISSANCE est une valeur date
.column().isNumber(false);                  // TAILLE est une valeur numérique
}

TestTable.java

@Test
public void testTypeOfValuesInColumn() {
assertThat(tableMembres)
.column().isOfType(ValueType.NUMBER, false) // ID est une valeur numérique
.column().isOfType(ValueType.TEXT, false)   // NOM est une valeur texte
.column().isText(false)                     // PRENOM est une valeur texte
.column().isOfType(ValueType.TEXT, true)    // SURNOM est une valeur texte
.isText(true)                      // Mais la valeur est parfois à null
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE, ValueType.NOT_IDENTIFIED)
.column().isDate(false)                     // DATE_NAISSANCE est une valeur date
.column().isNumber(true);                   // TAILLE est une valeur numérique (à null parfois)
}

TestRequest.java

@Test
public void testTypeOfValuesInColumn() {
assertThat(requestWithPks)
.column().isOfType(ValueType.TEXT, false)   // NOM_GROUPE est une valeur texte
.column().isText(false)                     // NOM_MEMBRE est une valeur texte
.column().isOfType(ValueType.TEXT, true)    // SURNOM est une valeur texte
.isText(true)                      // Mais la valeur est parfois à null
.isOfAnyTypeIn(ValueType.TEXT, ValueType.DATE, ValueType.NOT_IDENTIFIED)
.column().isNumber(false);                  // ID_MEMBRE est une valeur numérique
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-E-3 - Assertion sur la nullité des valeurs d'une colonne

La méthode hasOnlyNotNullValues() permet de vérifier que la colonne ne contient que des valeurs non null.
La méthode hasOnlyNullValues() (non présente dans les exemples ci-dessous, car le jeu de données ne le permet pas) permet de vérifier que la colonne ne contient que des valeurs null.

TestTable.java

@Test
public void testNullityInColumn() {
assertThat(tableMembres)
.column("nom").hasOnlyNotNullValues()
.column("prenom").hasOnlyNotNullValues()
.column("date_naissance").hasOnlyNotNullValues();
}

TestRequest.java

@Test
public void testNullityInColumn() {
assertThat(requestWithPks)
.column("NOM_groupe").hasOnlyNotNullValues()
.column("id_Membre").hasOnlyNotNullValues();
}

Il est possible de consulter plus d'informations dans la documentation officielle.

III-E-4 - Assertion sur l'égalité des valeurs d'une colonne

Les méthodes hasValues(...) permettent de vérifier que les valeurs contenues dans une colonne sont égales à celles en paramètre.
Comme pour la méthode isEqualTo(...), il est possible d'utiliser des chaînes de caractères pour comparer les valeurs (même pour des valeurs qui ne sont pas de type TEXT).

TestChanges.java

@Test
public void testEgalityOfValuesInColumn() {
assertThat(changesTableMembres)
.change(1).isModification()
.column().hasValues(5)
.column().hasValues("Sumner")
.column().hasValues("Gordon Matthew Thomas")
.column().hasValues(null, "Sting")
.column().hasValues(DateValue.of(1951, 10, 2))
.column().hasValues(1.82);
}

TestTable.java

@Test
public void testEgalityOfValuesInColumn() {
assertThat(tableMembres)
.column("nom")
.hasValues("Hewson",
"Evans",
"Clayton",
"Mullen",
"Sumner",
"Copeland",
"Padovani")
.column("date_naissance")
.hasValues(DateValue.of(1960, 5, 10),
DateValue.of(1961, 8, 8),
DateValue.of(1960, 3, 13),
DateValue.of(1961, 10, 31),
DateValue.of(1951, 10, 2),
DateValue.of(1952, 7, 16),
DateValue.of(1950, 10, 13))
.hasValues("1960-05-10",
"1961-08-08",
"1960-03-13",
"1961-10-31",
"1951-10-02",
"1952-07-16",
"1950-10-13");
}

TestRequest.java

@Test
public void testEgalityOfValuesInColumn() {
assertThat(requestWithPks)
.column("NOM_groupe")
.hasValues("U2",
"U2",
"U2",
"U2",
"The Police",
"The Police")
.column("NOM_membre")
.hasValues("Hewson",
"Evans",
"Clayton",
"Mullen",
"Sumner",
"Copeland");
}

Il est possible de consulter plus d'informations dans la documentation officielle.

IV - Conclusion

Comme nous avons pu le voir, AssertJ-DB offre déjà un grand nombre de manières de réaliser des tests unitaires avec une base donnée.

Ce projet est actif. De nouvelles fonctionnalités sont en cours de développement. Elles sont indiquées au fur et à mesure de leur achèvement dans la page du projet dédié aux nouvelles.

Vous pourrez également retrouver ces informations dans la documentation d'AssertJ-DB sur les concepts et les assertions.

V - Remerciements

Je remercie très sincèrement :

- www.developpez.com qui me permet de publier cet article ;

- Nono40 et djibril pour leurs outils ;

- Mickael Baron pour sa relecture technique ;

- Claude Leloup pour sa relecture orthographique.

Je remercie également Joel Costigliola pour son accueil et son aide dans le développement de mon projet.