finished sample ex_041_004

EFCore3_Reforged
Marc CHEVALDONNE 5 years ago
parent 35c719e7b4
commit 52b9f5eab6

@ -16,7 +16,20 @@ Pour le reste de l'exemple, celui-ci n'apporte rien de nouveau par rapport à l'
## Pourquoi autant de projets dans cet exemple ?
Quatre projets constituent cet exemple :
* **ex_041_004_TestingInMemory** est une bibliothèque de classes .NET Standard contentant le modèle, ie la classe ```Nounours``` et la classe ```NounoursContext```
* **ex_041_004_ConsoleTests_w_SqlServer** est une application console .NET Core qui prouve le fonctionnement "normal" de la base de données (ie comme dans l'exemple ex_041_001) en exploitant la bibliothèque de classes **ex_041_004_TestingInMemory**
* **ex_041_004_UnitTests_w_InMemory** est une application de tests unitaires xUnit exploitant le fournisseur **InMemory** et la bibliothèque de classes **ex_041_004_TestingInMemory**
* **ex_041_004_UnitTests_w_SQLiteInMemory** est une application de tests unitaires xUnit exploitant le fournisseur **SQLite in memory** et la bibliothèque de classes **ex_041_004_TestingInMemory**
Vous pouvez donc exécuter cet exemple de trois manières :
* via **ex_041_004_ConsoleTests_w_SqlServer** comme dans l'exemple ex_041_001 avec ```dotnet ef```
* via les tests unitaires de **ex_041_004_UnitTests_w_InMemory**
* via les tests unitaires de **ex_041_004_UnitTests_w_SQLiteInMemory**
## Comment a été construit cet exemple ?
### bibliothèque .NET Standard ex_041_004_TestingInMemory
Cet exemple est tout d'abord construit de la même manière que l'exemple *ex_041_001_ConnectionStrings*.
Il ne faut pas oublier les NuGet nécessaires :
* Microsoft.EntityFrameworkCore : pour le projet en général
@ -28,14 +41,14 @@ On obtient ainsi la classe ```NounoursContext``` suivante :
```csharp
using Microsoft.EntityFrameworkCore;
namespace ex_041_004_InMemory
namespace ex_041_004_TestingInMemory
{
public class NounoursContext : DbContext
{
public DbSet<Nounours> Nounours { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_InMemory.Nounours.mdf;Trusted_Connection=True;");
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_TestingInMemory.Nounours.mdf;Trusted_Connection=True;");
}
}
```
@ -46,7 +59,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_InMemory.Nounours.mdf;Trusted_Connection=True;");
options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_041_004_TestingInMemory.Nounours.mdf;Trusted_Connection=True;");
}
}
```
@ -67,7 +80,83 @@ public NounoursContext(DbContextOptions<NounoursContext> options)
```
La classe ```NounoursContext``` peut donc s'utiliser comme précédemment, sans changement, et la base *SqlServer* sera utilisée ; ou alors, on pourra injecter un autre fournisseur.
## Configuration du test unitaire
### application console .NET Core ex_041_004_ConsoleTests_w_SqlServer
L'application console .NET Core **ex_041_004_ConsoleTests_w_SqlServer** fait référence à la bibliothèque .NET Standard précédente pour pouvoir consommer ```Nounours``` et ```NounoursContext```.
Sa seule classe est donc ```Program``` et peut donc être codée de la manière suivante :
```csharp
using System;
using ex_041_004_TestingInMemory;
namespace ex_041_004_ConsoleTests_w_SqlServer
{
class Program
{
static void Main(string[] args)
{
Nounours chewie = new Nounours { Nom = "Chewbacca" };
Nounours yoda = new Nounours { Nom = "Yoda" };
Nounours ewok = new Nounours { Nom = "Ewok" };
using (var context = new NounoursContext())
{
// Crée des nounours et les insère dans la base
Console.WriteLine("Creates and inserts new Nounours");
context.Add(chewie);
context.Add(yoda);
context.Add(ewok);
context.SaveChanges();
}
}
}
}
```
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
* Ouvrez la *Console du Gestionnaire de package*, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
* Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
```
cd .\p08_BDD_EntityFramework\ex_041_004_ConsoleTests_w_SqlServer
```
*Note*:
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
```dotnet tool install --global dotnet-ef```
* Migration : comme la classe dérivant de ```DbContext``` n'est pas dans l'application Console, nous devons préciser dans quel projet elle se trouve en ajoutant ```--project ../ex_041_004_TestingInMemory```.
```
dotnet ef migrations add migration_ex_041_004 --project ../ex_041_004_TestingInMemory
```
* Création de la table : comme pour la migration, il faut préciser dans quel projet se trouve l'instance de ```DbContext```.
```
dotnet ef database update --project ../ex_041_004_TestingInMemory
```
* Génération et exécution
Vous pouvez maintenant générer et exécuter l'exemple **ex_041_004_ConsoleTests_w_SqlServer**.
* Comment vérifier le contenu des bases de données SQL Server et SQLite ?
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* Déployez dans l'*Explorateur d'objets SQL Server* :
* *SQL Server*,
* puis *(localdb)\MSSQLLocalDB ...*,
* puis *Bases de données*
* puis celle portant le nom de votre migration, dans mon cas : *ex_041_004_TestingInMemory.Nounours.mdf*
* puis *Tables*
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :
|Id |Nom
|---|---
|1|Chewbacca
|2|Yoda
|3|Ewok
### Configuration des tests unitaires avec InMemory
Le test unitaire est de type *xUnit* et va permettre d'injecter le fournisseur **InMemory**.
* On crée un nouveau projet de tests unitaires (*xUnit*)
* On lui ajoute le package NuGet : *Microsoft.EntityFrameworkCore.InMemory*
@ -90,7 +179,7 @@ namespace ex_041_004_UnitTests
.UseInMemoryDatabase(databaseName: "Add_Test_database")
.Options;
// runs the test against one instance of the context
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
Nounours chewie = new Nounours { Nom = "Chewbacca" };
@ -103,7 +192,7 @@ namespace ex_041_004_UnitTests
context.SaveChanges();
}
// Use a separate instance of the context to verify correct data was saved to database
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
Assert.Equal(3, context.Nounours.Count());
@ -127,7 +216,7 @@ Assert.Equal("Chewbacca", context.Nounours.First().Nom);
Notez que le choix du fournisseur est bien fait au démarrage du test avec la création du ```DbContextOptionsBuilder``` :
```csharp
var options = new DbContextOptionsBuilder<NounoursContext>()
.UseInMemoryDatabase(databaseName: "Add_writes_to_database")
.UseInMemoryDatabase(databaseName: "Add_Test_database")
.Options;
```
et que l'injection est effectuée plus bas :
@ -147,7 +236,7 @@ public void Modify_Test()
.UseInMemoryDatabase(databaseName: "Modify_Test_database")
.Options;
// Run the test against one instance of the context
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
Nounours chewie = new Nounours { Nom = "Chewbacca" };
@ -160,25 +249,25 @@ public void Modify_Test()
context.SaveChanges();
}
// Use a separate instance of the context to verify correct data was saved to database
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
string nameToFind = "ew";
Assert.Equal(2, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
nameToFind = "ewo";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
var ewok = context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).First();
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
ewok.Nom = "Wicket";
context.SaveChanges();
}
// Use a separate instance of the context to verify correct data was saved to database
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
string nameToFind = "ew";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
nameToFind = "wick";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
}
}
```
@ -186,16 +275,16 @@ Ce cas de test :
* vérifie d'abord qu'il y a deux nounours dont le nom contient la chaîne "ew",
```csharp
string nameToFind = "ew";
Assert.Equal(2, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
```
* vérifie qu'il y a un nounours dont le nom contient la chaîne "ewo"
```csharp
nameToFind = "ewo";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
```
* modifie le nom de ce nounours en "Wicket"
```csharp
var ewok = context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).First();
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
ewok.Nom = "Wicket";
```
* enregistre les changements
@ -205,14 +294,144 @@ context.SaveChanges();
* vérifie ensuite qu'il n'y a plus qu'un Nounours dont le nom contient la chaîne "ew"
```csharp
string nameToFind = "ew";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
```
* vérifie qu'il y a un Nounours dont le nom contient la chaîne "wick"
```csharp
nameToFind = "wick";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.Contains(nameToFind, System.StringComparison.CurrentCultureIgnoreCase)).Count());
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
```
### Configuration des tests unitaires avec SQLite in memory
Le projet se construit exactement da la même manière que le précédent à quelques exceptions près que voici.
* package NuGet :
à la place du NuGet *Microsoft.EntityFrameworkCore.InMemory*, il faut ajouter *Microsoft.EntityFrameworkCore.Sqlite*
* ouverture de la connexion :
au début des tests, il faut penser à ouvrir la connexion avec la base en mémoire SQLite qui doit rester ouverte durant tout le test.
```csharp
//connection must be opened to use In-memory database
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
```
* avant de commencer à traiter avec la base en mémoire, on peut vérifier qu'elle a bien été créée :
```csharp
//context.Database.OpenConnection();
context.Database.EnsureCreated();
```
Au final, les tests ressemblent à :
```csharp
using ex_041_004_TestingInMemory;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Xunit;
using Microsoft.Data.Sqlite;
namespace ex_041_004_UnitTests_w_SQLiteInMemory
{
public class NounoursDB_Tests
{
[Fact]
public void Add_Test()
{
//connection must be opened to use In-memory database
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<NounoursContext>()
.UseSqlite(connection)
.Options;
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
//context.Database.OpenConnection();
context.Database.EnsureCreated();
Nounours chewie = new Nounours { Nom = "Chewbacca" };
Nounours yoda = new Nounours { Nom = "Yoda" };
Nounours ewok = new Nounours { Nom = "Ewok" };
context.Nounours.Add(chewie);
context.Nounours.Add(yoda);
context.Nounours.Add(ewok);
context.SaveChanges();
}
//uses another instance of the context to do the tests
using (var context = new NounoursContext(options))
{
context.Database.EnsureCreated();
Assert.Equal(3, context.Nounours.Count());
Assert.Equal("Chewbacca", context.Nounours.First().Nom);
}
}
[Fact]
public void Modify_Test()
{
//connection must be opened to use In-memory database
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<NounoursContext>()
.UseSqlite(connection)
.Options;
//prepares the database with one instance of the context
using (var context = new NounoursContext(options))
{
//context.Database.OpenConnection();
context.Database.EnsureCreated();
Nounours chewie = new Nounours { Nom = "Chewbacca" };
Nounours yoda = new Nounours { Nom = "Yoda" };
Nounours ewok = new Nounours { Nom = "Ewok" };
context.Nounours.Add(chewie);
context.Nounours.Add(yoda);
context.Nounours.Add(ewok);
context.SaveChanges();
}
//uses another instance of the context to do the tests
using (var context = new NounoursContext(options))
{
context.Database.EnsureCreated();
string nameToFind = "ew";
Assert.Equal(2, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
nameToFind = "wo";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
var ewok = context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).First();
ewok.Nom = "Wicket";
context.SaveChanges();
}
//uses another instance of the context to do the tests
using (var context = new NounoursContext(options))
{
context.Database.EnsureCreated();
string nameToFind = "ew";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
nameToFind = "wick";
Assert.Equal(1, context.Nounours.Where(n => n.Nom.ToLower().Contains(nameToFind)).Count());
}
}
}
}
```
### exécution des tests unitaires
Vous pouvez maintenant exécuter les tests unitaires via l'*Eplorateur de tests*.
* Dans le menu *Test*, choisissez *Explorateur de tests*
<img src="./readmefiles/readme_01.png" width="460"/>
* Cliquez sur "Exécuter tous les tests"
<img src="./readmefiles/readme_02.png" width="460"/>
* Observez le bon fonctionnement
---
Copyright &copy; 2019-2020 Marc Chevaldonné

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Loading…
Cancel
Save