快照器是.NET Core和.NET Framework的快照测试工具
快照器是一种灵活的快照测试工具,可简化.NET中的单元测试中的结果验证。它基于开玩笑的快照测试的想法。
要获取有关快照者的更多详细信息,请访问SnapShooter Docs
要开始,请安装SnapShooter Xunit或Nunit Nuget软件包:
dotnet add package Snapshooter.Xunitdotnet add package Snapshooter.NUnitdotnet add package Snapshooter.MSTest开始
要通过单元测试中的快照来确定您的测试结果,请按照以下步骤:
插入快照声明Snapshot.Match(yourResultObject);进入您的单元测试。
例子:
/// <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 ) ;
} Snapshot.Match(person)语句创建了结果对象的新快照,并将其存储在__snapshots__文件夹中。 __snapshots__文件夹始终在您执行的单元测试文件旁边。
快照名称: <UnitTestClassName>.<TestMethodName>.snap
查看您的新快照文件__snapshots__/<UnitTestClassName>.<TestMethodName>.snap 。
现在, Snapshot.Match(person)语句将再次创建测试结果快照,并将其与__snapshots__文件夹中的评论快照进行比较。 __snapshots__文件夹始终在您执行的单元测试文件旁边。
如果您的结果对象已更改并且现有快照不再匹配,则单位测试将失败。单位测试错误消息将指向快照中确切的不匹配位置。
此外,将创建带有名称__mismatch__子文件夹的__snapshots__文件夹中。在此文件夹中,您可以找到与__snapshots__文件夹中现有快照不匹配的实际快照。因此,可以将两个快照与文本比较工具进行比较。
如果不匹配文件夹中的快照__mismatch__正确,只需将其移至parent __snapshots__文件夹(覆盖现有的一个)即可。
阅读更多
快照的默认匹配语法是:
Snapshot . Match ( person ) ;但是,我们也可以使用流利的语法:
person . MatchSnapshot ( ) ;或者我们可以使用FluentAssertion的sys()语法:
person . Should ( ) . MatchSnapshot ( ) ;对于Nunit,我们将支持声音(即将推出):
Assert . That ( person , Match . Snapshot ( ) ) ; 如果在快照断言中应忽略快照中的某些字段,则可以使用以下忽略选项:
[ 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" ) ) ;
}要忽略的字段将通过JSONPATH定位,因此您非常灵活,您也可以忽略子对象或数组中的字段。
忽略示例:
// 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" ) ) ; 如果我们想以特定名称忽略所有字段,那么我们有两个选项:
选项1:使用忽略匹配选项“ ignoreallfields()”并添加名称。
// Ignores all fields with name 'Id'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreAllFields ( "Id" ) ) ;选项2:使用以下JSONPATH语法**使用默认的忽略匹配选项“ ignorefields(**。)”。
// Ignores all fields with name 'Id'
Snapshot . Match < Person > ( person , matchOptions => matchOptions . IgnoreFields ( "**.Id" ) ) ;如果我们的快照的某些字段太大,例如一个带有大量数据的二进制字段,那么我们可以使用HashField选项。哈希菲尔德选项创建了场值的哈希,因此每次比较哈希。如果字段值发生变化,则快照匹配将失败。
[ Fact ]
public void ImageSnapshot_HashImageBinary ( )
{
// arrange
var serviceClient = new ServiceClient ( ) ;
// act
TestImage image = serviceClient . CreateMonaLisaImage ( ) ;
// assert
Snapshot . Match ( image , matchOptions => matchOptions . HashField ( "Data" ) ) ;
}示例快照与哈希
{
"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= "
}hash的字段可以通过JSONPATH或字段名称位于。
哈希现场示例:
// 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" ) ) ;有时,有一个快照中的字段,您需要与其他值分开断言。
例如,“人”的ID字段始终是在服务中新生成的,因此您在测试中始终收到具有新ID的人(GUID)。现在,如果您想检查ID不是空的GUID,则可以使用Assert选项。
/// <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" ) ) ) ) ;
}要断言的字段将通过JSONPATH定位,因此您非常灵活,您也可以忽略子对象或数组中的字段。
断言示例:
// 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[*]" ) ) ) ) ;快照器断言功能不限于Xunit或Nunit断言,也可以使用流利的断言或其他断言工具。
可以将所有忽略,ISTYPE或断言字段检查串联。
[ 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" ) ) ) ) ;
} 在CI构建中运行快照测试时,您可能需要确保正确签入快照,因为否则没有快照的测试将创建初始快照并变为绿色。
为了使您的CI构建没有快照的测试失败,您可以通过将环境变量SNAPSHOOTER_STRICT_MODE设置为on或true ,将快照器行为设置为严格模式。
该项目采用了贡献者盟约定义的行为准则,以阐明我们社区的预期行为。有关更多信息,请参阅《瑞士生活OSS行为准则》。