Snapshooter est un outil de test d'instantané pour .NET Core et .NET Framework
Snapshooter est un outil de test d'instantané flexible pour simplifier la validation des résultats dans vos tests unitaires dans .NET. Il est basé sur l'idée des tests d'instantané de plaisanterie.
Pour obtenir des informations plus détaillées sur Snapshooter, accédez aux documents d'instantané
Pour commencer, installez le package Snaphooter Xunit ou Nunit NuGet:
dotnet add package Snapshooter.Xunitdotnet add package Snapshooter.NUnitdotnet add package Snapshooter.MSTestCommencer
Pour affirmer vos résultats de test avec des instantanés dans vos tests unitaires, suivez les étapes suivantes:
Insérez un instant d'instantané Affirmage instantanée Snapshot.Match(yourResultObject); dans votre test unitaire.
Exemple:
/// <summary>
/// Tests if the new created person is valid.
/// </summary>
[ Fact ]
public void CreatePersonSnapshotTest ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestPerson person = serviceClient . CreatePerson (
Guid . Parse ( "2292F21C-8501-4771-A070-C79C7C7EF451" ) , "David" , "Mustermann" ) ;
// assert
Snapshot . Match ( person ) ;
} L'instruction Snapshot.Match(person) crée un nouveau instantané de votre objet de résultat et le stocke dans le dossier __snapshots__ . Le dossier __snapshots__ est toujours à côté de votre fichier de test unitaire exécuté.
Nom de <UnitTestClassName>.<TestMethodName>.snap instant
Passez en revue votre nouveau fichier instantané __snapshots__/<UnitTestClassName>.<TestMethodName>.snap .
Maintenant, l'instruction Snapshot.Match(person) créera à nouveau un instantané de votre résultat de test et le comparera avec votre instantané examiné dans le dossier __snapshots__ . Le dossier __snapshots__ est toujours à côté de votre fichier de test unitaire exécuté.
Si votre objet de résultat a changé et que l'instantané existant ne correspond plus, le test unitaire échouera. Le message d'erreur de test unitaire pointera vers la position de non-correspondance exacte dans l'instantané.
De plus, dans le dossier instantané __snapshots__ un sous-dossier avec nom __mismatch__ sera créé. Dans ce dossier, vous pouvez trouver l'instantané réel qui est incompatible avec l'instantané existant dans le dossier __snapshots__ . Par conséquent, il est possible de comparer les deux instantanés avec un outil de comparaison de texte.
Si l'instantané dans le dossier de décalage __mismatch__ est correct, déplacez-le simplement dans le dossier parent __snapshots__ (remplacer le existant).
En savoir plus
La syntaxe de correspondance par défaut pour les instantanés est:
Snapshot . Match ( person ) ;Cependant, nous pourrions également utiliser la syntaxe fluide:
person . MatchSnapshot ( ) ;Ou nous pouvons utiliser la syntaxe de FluentAssertion devrait ():
person . Should ( ) . MatchSnapshot ( ) ;Pour Nunit, nous soutiendrons l'affirmement. Cette syntaxe (à venir bientôt):
Assert . That ( person , Match . Snapshot ( ) ) ; Si certains champs de votre instantané doivent être ignorés lors de l'affirmation d'instantané, les options d'ignore suivantes peuvent être utilisées:
[ Fact ]
public void CreatePersonSnapshot_IgnoreId ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestPerson person = serviceClient . CreatePerson ( "Hans" , "Muster" ) ;
// assert
Snapshot . Match < Person > ( testPerson , matchOptions => matchOptions . IgnoreField ( "Size" ) ) ;
}Les champs à ignorer seront situés via JSONPATH, donc vous êtes très flexible et vous pouvez également ignorer les champs des objets enfants ou des tableaux.
Ignorez des exemples:
// Ignores the field 'StreetNumber' of the child node 'Address' of the person
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreField ( "Address.StreetNumber" ) ) ;
// Ignores the field 'Name' of the child node 'Country' of the child node 'Address' of the person
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreField ( "Address.Country.Name" ) ) ;
// Ignores the field 'Id' of the first person in the 'Relatives' array of the person
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreField ( "Relatives[0].Id" ) ) ;
// Ignores the field 'Name' of all 'Children' nodes of the person
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreField ( "Children[*].Name" ) ) ;
// Ignores all fields with name 'Id'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreField ( "**.Id" ) ) ; Si nous voulons ignorer tous les champs par un nom spécifique, nous avons deux options:
Option 1: Utilisez l'option Ignore Match 'IgnoreALLFields ()' et ajoutez le nom.
// Ignores all fields with name 'Id'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreAllFields ( "Id" ) ) ;Option 2: Utilisez l'option Ignore Match par défaut 'Ignorefields (**.)' Avec la syntaxe JSONPath suivante **.
// Ignores all fields with name 'Id'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreFields ( "**.Id" ) ) ;Si certains champs de notre instantané sont trop grands, par exemple un champ binaire avec beaucoup de données, nous pouvons utiliser l'option Hashfield. L'option Hashfield crée un hachage de la valeur du champ et donc chaque fois que seul le hachage est comparé. S'il y a un changement dans la valeur du champ, la correspondance instantanée échouera.
[ Fact ]
public void ImageSnapshot_HashImageBinary ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestImage image = serviceClient . CreateMonaLisaImage ( ) ;
// assert
Snapshot . Match ( image , matchOptions => matchOptions . HashField ( "Data" ) ) ;
}Exemple instantané avec hachage
{
"Id" : 3450987 ,
"OwnerId" : " 0680faef-6e89-4d52-bad8-291053c66696 " ,
"Name" : " Mona Lisa " ,
"CreationDate" : " 2020-11-10T21:23:09.036+01:00 " ,
"Price" : 951868484.345 ,
"Data" : " m+sQR9KG9WpgYoQiRASPkt9FLJOLsjK86UuiXKVRzas= "
}Le ou les champs à hachage peuvent être situés via JSONPATH ou via le nom du champ.
Exemples de champ de hachage:
// Hash the field 'Data' of the child node 'Thumbnail' of the person
Snapshot . Match < Person > ( person , matchOptions => matchOptions . HashField ( "Thumbnail.Data" ) ) ;
// Hash the field 'Data' of the first thumbnail in the 'Thumbnails' array of the image
Snapshot . Match < Person > ( person , matchOptions => matchOptions . HashField ( "Thumbnails[0].Data" ) ) ;
// Ignores the field 'Data' of all 'Thumbnails' nodes of the image
Snapshot . Match < Person > ( person , matchOptions => matchOptions . HashField ( "Thumbnails[*].Data" ) ) ;
// Ignores all fields with name 'Data'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . HashField ( "**.Data" ) ) ;Parfois, il y a des champs dans un instantané, que vous souhaitez affirmer séparément contre une autre valeur.
Par exemple, le champ ID d'une «personne» est toujours nouvellement généré dans un service, donc vous recevez dans le test toujours une personne avec un nouvel ID (GUID). Maintenant, si vous souhaitez vérifier que l'ID n'est pas un GUID vide, l'option Assert peut être utilisée.
/// <summary>
/// Tests if the new created person is valid and the person id is not empty.
/// </summary>
[ Fact ]
public void CreatePersonSnapshot_AssertId ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestPerson person = serviceClient . CreatePerson ( "Hans" , "Muster" ) ; // --> id is created within the service
// assert
Snapshot . Match < Person > ( testPerson , matchOption => matchOption . Assert (
fieldOption => Assert . NotEqual ( Guid . Empty , fieldOption . Field < Guid > ( "Id" ) ) ) ) ;
}Les champs à affirmer seront situés via JSONPATH, donc vous êtes très flexible et vous pouvez également ignorer les champs des objets enfants ou des tableaux.
Affirmer des exemples:
// Assert the field 'Street' of the 'Address' of the person
Snapshot . Match < Person > ( person , matchOption => matchOption . Assert (
fieldOption => Assert . Equal ( 15 , fieldOption . Field < int > ( "Address.StreetNumber" ) ) ) ) ;
// Asserts the field 'Code' of the field 'Country' of the 'Address' of the person
Snapshot . Match < Person > ( person , matchOption => matchOption . Assert (
fieldOption => Assert . Equal ( "De" , fieldOption . Field < CountryCode > ( "Address.Country.Code" ) ) ) ) ;
// Asserts the fist 'Id' field of the 'Relatives' array of the person
Snapshot . Match < Person > ( person , > matchOption . Assert (
fieldOption => Assert . NotNull ( fieldOption . Field < string > ( "Relatives[0].Id" ) ) ) ) ;
// Asserts every 'Id' field of all the 'Relatives' of the person
Snapshot . Match < Person > ( person , > matchOption . Assert (
fieldOption => Assert . NotNull ( fieldOption . Fields < string > ( "Relatives[*].Id" ) ) ) ) ;
// Asserts 'Relatives' array is not empty
Snapshot . Match < Person > ( person , > matchOption . Assert (
fieldOption => Assert . NotNull ( fieldOption . Fields < TestPerson > ( "Relatives[*]" ) ) ) ) ;La fonctionnalité affirmative d'instantané ne se limite pas aux affirmations Xunit ou Nunit, il pourrait également être utilisé des affirmations courantes ou un autre outil d'asservation.
Tous les vérifications de champ ignorant, issype ou affirmant peuvent être concaténées.
[ Fact ]
public void Match_ConcatenateFieldChecksTest_SuccessfulMatch ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestPerson person = serviceClient . CreatePerson ( "Hans" , "Muster" ) ;
// act & assert
Snapshot . Match < TestPerson > ( testPerson , matchOption => matchOption
. Assert ( option => Assert . NotEqual ( Guid . Empty , option . Field < Guid > ( "Id" ) ) )
. IgnoreField < DateTime > ( "CreationDate" )
. Assert ( option => Assert . Equal ( - 58 , option . Field < int > ( "Address.StreetNumber" ) ) )
. Assert ( option => testChild . Should ( ) . BeEquivalentTo ( option . Field < TestChild > ( "Children[3]" ) ) )
. IgnoreField < TestCountry > ( "Address.Country" )
. Assert ( option => Assert . Null ( option . Field < TestCountry > ( "Relatives[0].Address.Plz" ) ) ) ) ;
} Lors de l'exécution de tests de jet d'instantané dans une construction CI, vous voudrez peut-être vous assurer qu'un instantané est correctement enregistré, car les tests autrement sans instantané ne feront que créer l'instantané initial et devenir vert.
Afin d'échouer des tests qui sont sans instantané sur votre CI-Build, vous pouvez définir le comportement de l'instantané sur STRICT-Mode en définissant la variable d'environnement SNAPSHOOTER_STRICT_MODE sur on ON ON OU true .
Ce projet a adopté le code de conduite défini par le Contributeur Covenant pour clarifier le comportement attendu dans notre communauté. Pour plus d'informations, consultez le code de conduite OSS de la vie suisse.