13 KiB
Verify.EntityFramework
Extends Verify to allow verification of EntityFramework bits.
Support is available via a Tidelift Subscription.
Part of the .NET Foundation
Contents
NuGet package
- https://nuget.org/packages/Verify.EntityFramework/
- https://nuget.org/packages/Verify.EntityFrameworkClassic/
Enable
Enable VerifyEntityFramewok once at assembly load time:
EF Core
VerifyEntityFramework.Enable();
EF Classic
VerifyEntityFrameworkClassic.Enable();
Recording
Recording allows all commands executed by EF to be captured and then (optionally) verified.
Enable
Call SqlRecording.EnableRecording()
on DbContextOptionsBuilder
.
var builder = new DbContextOptionsBuilder<SampleDbContext>();
builder.UseSqlServer(connection);
builder.EnableRecording();
var data = new SampleDbContext(builder.Options);
EnableRecording
should only be called in the test context.
Usage
On the DbContext
call SqlRecording.StartRecording()
to start recording.
var company = new Company
{
Content = "Title"
};
data.Add(company);
await data.SaveChangesAsync();
SqlRecording.StartRecording();
await data.Companies
.Where(x => x.Content == "Title")
.ToListAsync();
await Verifier.Verify(data.Companies.Count());
Will result in the following verified file:
{
target: '5',
sql: [
{
Type: 'ReaderExecutedAsync',
Text: "SELECT [c].[Id], [c].[Content]
FROM [Companies] AS [c]
WHERE [c].[Content] = N'Title'"
},
{
Type: 'ReaderExecuted',
Text: 'SELECT COUNT(*)
FROM [Companies] AS [c]'
}
]
}
DbContext spanning
StartRecording
can be called on different DbContext instances (built from the same options) and the results will be aggregated.
var builder = new DbContextOptionsBuilder<SampleDbContext>();
builder.UseSqlServer(connectionString);
builder.EnableRecording();
await using var data1 = new SampleDbContext(builder.Options);
SqlRecording.StartRecording();
var company = new Company
{
Content = "Title"
};
data1.Add(company);
await data1.SaveChangesAsync();
await using var data2 = new SampleDbContext(builder.Options);
await data2.Companies
.Where(x => x.Content == "Title")
.ToListAsync();
await Verifier.Verify(data2.Companies.Count());
{
target: '5',
sql: [
{
Type: 'ReaderExecutedAsync',
HasTransaction: true,
Parameters: {
@p0: 0,
@p1: 'Title'
},
Text: 'SET NOCOUNT ON;
INSERT INTO [Companies] ([Id], [Content])
VALUES (@p0, @p1);'
},
{
Type: 'ReaderExecutedAsync',
Text: "SELECT [c].[Id], [c].[Content]
FROM [Companies] AS [c]
WHERE [c].[Content] = N'Title'"
},
{
Type: 'ReaderExecuted',
Text: 'SELECT COUNT(*)
FROM [Companies] AS [c]'
}
]
}
ChangeTracking
Added, deleted, and Modified entities can be verified by performing changes on a DbContext and then verifying the instance of ChangeTracking. This approach leverages the EntityFramework ChangeTracker.
Added entity
This test:
[Test]
public async Task Added()
{
var options = DbContextOptions();
await using var data = new SampleDbContext(options);
var company = new Company
{
Content = "before"
};
data.Add(company);
await Verifier.Verify(data.ChangeTracker);
}
Will result in the following verified file:
{
Added: {
Company: {
Id: 0,
Content: 'before'
}
}
}
Deleted entity
This test:
[Test]
public async Task Deleted()
{
var options = DbContextOptions();
await using var data = new SampleDbContext(options);
data.Add(new Company {Content = "before"});
await data.SaveChangesAsync();
var company = data.Companies.Single();
data.Companies.Remove(company);
await Verifier.Verify(data.ChangeTracker);
}
Will result in the following verified file:
{
Deleted: {
Company: {
Id: 0
}
}
}
Modified entity
This test:
[Test]
public async Task Modified()
{
var options = DbContextOptions();
await using var data = new SampleDbContext(options);
var company = new Company
{
Content = "before"
};
data.Add(company);
await data.SaveChangesAsync();
data.Companies.Single().Content = "after";
await Verifier.Verify(data.ChangeTracker);
}
Will result in the following verified file:
{
Modified: {
Company: {
Id: 0,
Content: {
Original: 'before',
Current: 'after'
}
}
}
}
Queryable
This test:
var queryable = data.Companies
.Where(x => x.Content == "value");
await Verifier.Verify(queryable);
Will result in the following verified file:
EF Core
SELECT [c].[Id], [c].[Content]
FROM [Companies] AS [c]
WHERE [c].[Content] = N'value'
EF Classic
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Content] AS [Content]
FROM [dbo].[Companies] AS [Extent1]
WHERE N'value' = [Extent1].[Content]
AllData
This test:
var settings = new VerifySettings();
settings.ModifySerialization(
serialization =>
serialization.AddExtraSettings(
serializer =>
serializer.TypeNameHandling = TypeNameHandling.Objects));
await Verifier.Verify(data.AllData(), settings);
Will result in the following verified file with all data in the database:
[
{
$type: 'Company',
Id: 1,
Content: 'Company1'
},
{
$type: 'Company',
Id: 4,
Content: 'Company2'
},
{
$type: 'Company',
Id: 6,
Content: 'Company3'
},
{
$type: 'Company',
Id: 7,
Content: 'Company4'
},
{
$type: 'Employee',
Id: 2,
CompanyId: 1,
Content: 'Employee1',
Age: 25
},
{
$type: 'Employee',
Id: 3,
CompanyId: 1,
Content: 'Employee2',
Age: 31
},
{
$type: 'Employee',
Id: 5,
CompanyId: 4,
Content: 'Employee4',
Age: 34
}
]
Security contact information
To report a security vulnerability, use the Tidelift security contact. Tidelift will coordinate the fix and disclosure.
Icon
Database designed by Creative Stall from The Noun Project.