This commit is contained in:
Родитель
bec69af890
Коммит
bcf2f85fdf
140
readme.md
140
readme.md
|
@ -7,17 +7,19 @@ To change this file edit the source file and then run MarkdownSnippets.
|
|||
|
||||
# <img src="/src/icon.png" height="30px"> Verify.EntityFramework
|
||||
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/eedjhmx5o3082tyq?svg=true)](https://ci.appveyor.com/project/SimonCropp/verify-entityframework)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/g6njwv0aox62atu0?svg=true)](https://ci.appveyor.com/project/SimonCropp/verify-entityframework)
|
||||
[![NuGet Status](https://img.shields.io/nuget/v/Verify.EntityFramework.svg)](https://www.nuget.org/packages/Verify.EntityFramework/)
|
||||
|
||||
Extends [Verify](https://github.com/SimonCropp/Verify) to allow verification of web bits.
|
||||
Extends [Verify](https://github.com/SimonCropp/Verify) to allow verification of EntityFramewok bits.
|
||||
|
||||
|
||||
<!-- toc -->
|
||||
## Contents
|
||||
|
||||
* [Usage](#usage)
|
||||
* [Controller](#controller)<!-- endtoc -->
|
||||
* [Added entry](#added-entry)
|
||||
* [Deleted entry](#deleted-entry)
|
||||
* [Modified entry](#modified-entry)<!-- endtoc -->
|
||||
|
||||
|
||||
## NuGet package
|
||||
|
@ -27,7 +29,7 @@ https://nuget.org/packages/Verify.EntityFramework/
|
|||
|
||||
## Usage
|
||||
|
||||
Enable VerifyWeb once at assembly load time:
|
||||
Enable VerifyEntityFramewok once at assembly load time:
|
||||
|
||||
<!-- snippet: Enable -->
|
||||
<a id='snippet-enable'/></a>
|
||||
|
@ -38,21 +40,137 @@ VerifyEntityFramework.Enable();
|
|||
<!-- endsnippet -->
|
||||
|
||||
|
||||
### Controller
|
||||
|
||||
Given the following controller:
|
||||
|
||||
//snippet: MyController.cs
|
||||
### Added entry
|
||||
|
||||
This test:
|
||||
|
||||
//snippet: MyControllerTest
|
||||
<!-- snippet: Added -->
|
||||
<a id='snippet-added'/></a>
|
||||
```cs
|
||||
[Fact]
|
||||
public async Task Added()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using var context = new SampleDbContext(options);
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
await Verify(context);
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.cs#L12-L22' title='File snippet `added` was extracted from'>snippet source</a> | <a href='#snippet-added' title='Navigate to start of snippet `added`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
//snippet: MyControllerTests.Test.verified.txt
|
||||
<!-- snippet: Tests.Added.verified.txt -->
|
||||
<a id='snippet-Tests.Added.verified.txt'/></a>
|
||||
```txt
|
||||
{
|
||||
Added: {
|
||||
Company: {
|
||||
Id: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.Added.verified.txt#L1-L7' title='File snippet `Tests.Added.verified.txt` was extracted from'>snippet source</a> | <a href='#snippet-Tests.Added.verified.txt' title='Navigate to start of snippet `Tests.Added.verified.txt`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
|
||||
### Deleted entry
|
||||
|
||||
This test:
|
||||
|
||||
<!-- snippet: Deleted -->
|
||||
<a id='snippet-deleted'/></a>
|
||||
```cs
|
||||
[Fact]
|
||||
public async Task Deleted()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
var company = context.Companies.Single();
|
||||
context.Companies.Remove(company);
|
||||
await Verify(context);
|
||||
}
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.cs#L24-L43' title='File snippet `deleted` was extracted from'>snippet source</a> | <a href='#snippet-deleted' title='Navigate to start of snippet `deleted`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
<!-- snippet: Tests.Deleted.verified.txt -->
|
||||
<a id='snippet-Tests.Deleted.verified.txt'/></a>
|
||||
```txt
|
||||
{
|
||||
Deleted: {
|
||||
Company: {
|
||||
Id: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.Deleted.verified.txt#L1-L7' title='File snippet `Tests.Deleted.verified.txt` was extracted from'>snippet source</a> | <a href='#snippet-Tests.Deleted.verified.txt' title='Navigate to start of snippet `Tests.Deleted.verified.txt`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
|
||||
### Modified entry
|
||||
|
||||
This test:
|
||||
|
||||
<!-- snippet: Modified -->
|
||||
<a id='snippet-modified'/></a>
|
||||
```cs
|
||||
[Fact]
|
||||
public async Task Modified()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Single().Content = "after";
|
||||
await Verify(context);
|
||||
}
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.cs#L45-L63' title='File snippet `modified` was extracted from'>snippet source</a> | <a href='#snippet-modified' title='Navigate to start of snippet `modified`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
<!-- snippet: Tests.Modified.verified.txt -->
|
||||
<a id='snippet-Tests.Modified.verified.txt'/></a>
|
||||
```txt
|
||||
{
|
||||
Modified: {
|
||||
Company: {
|
||||
Id: 0,
|
||||
Content: {
|
||||
Original: 'before',
|
||||
Current: 'after'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<sup><a href='/src/Tests/Tests.Modified.verified.txt#L1-L11' title='File snippet `Tests.Modified.verified.txt` was extracted from'>snippet source</a> | <a href='#snippet-Tests.Modified.verified.txt' title='Navigate to start of snippet `Tests.Modified.verified.txt`'>anchor</a></sup>
|
||||
<!-- endsnippet -->
|
||||
|
||||
|
||||
## Icon
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# <img src="/src/icon.png" height="30px"> Verify.EntityFramework
|
||||
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/eedjhmx5o3082tyq?svg=true)](https://ci.appveyor.com/project/SimonCropp/verify-entityframework)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/g6njwv0aox62atu0?svg=true)](https://ci.appveyor.com/project/SimonCropp/verify-entityframework)
|
||||
[![NuGet Status](https://img.shields.io/nuget/v/Verify.EntityFramework.svg)](https://www.nuget.org/packages/Verify.EntityFramework/)
|
||||
|
||||
Extends [Verify](https://github.com/SimonCropp/Verify) to allow verification of web bits.
|
||||
Extends [Verify](https://github.com/SimonCropp/Verify) to allow verification of EntityFramewok bits.
|
||||
|
||||
|
||||
toc
|
||||
|
@ -16,26 +16,43 @@ https://nuget.org/packages/Verify.EntityFramework/
|
|||
|
||||
## Usage
|
||||
|
||||
Enable VerifyWeb once at assembly load time:
|
||||
Enable VerifyEntityFramewok once at assembly load time:
|
||||
|
||||
snippet: Enable
|
||||
|
||||
|
||||
### Controller
|
||||
|
||||
Given the following controller:
|
||||
|
||||
//snippet: MyController.cs
|
||||
### Added entry
|
||||
|
||||
This test:
|
||||
|
||||
//snippet: MyControllerTest
|
||||
snippet: Added
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
//snippet: MyControllerTests.Test.verified.txt
|
||||
snippet: Tests.Added.verified.txt
|
||||
|
||||
|
||||
### Deleted entry
|
||||
|
||||
This test:
|
||||
|
||||
snippet: Deleted
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
snippet: Tests.Deleted.verified.txt
|
||||
|
||||
|
||||
### Modified entry
|
||||
|
||||
This test:
|
||||
|
||||
snippet: Modified
|
||||
|
||||
Will result in the following verified file:
|
||||
|
||||
snippet: Tests.Modified.verified.txt
|
||||
|
||||
|
||||
## Icon
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
result: [
|
||||
{
|
||||
Value: 'Value1'
|
||||
},
|
||||
{
|
||||
Value: 'Value2'
|
||||
}
|
||||
],
|
||||
context: {
|
||||
Headers: {
|
||||
headerKey: 'headerValue',
|
||||
receivedInput: 'inputValue'
|
||||
},
|
||||
Cookies: {
|
||||
cookieKey: 'cookieValue'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
using VerifyXunit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
public class Usage :
|
||||
VerifyBase
|
||||
{
|
||||
#region MyControllerTest
|
||||
[Fact]
|
||||
public async Task ChangeTracked()
|
||||
{
|
||||
var database = await DbContextBuilder.GetDatabase("ChangeTracked");
|
||||
var employee = await database.Context.Employees.FindAsync(3);
|
||||
employee.Age++;
|
||||
await Verify(database.Context);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Usage(ITestOutputHelper output) :
|
||||
base(output)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
Added: {
|
||||
Company: {
|
||||
Id: 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
result: {
|
||||
ResultType: 'ChallengeResult',
|
||||
Scheme: 'scheme',
|
||||
Properties: {
|
||||
key: 'value'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
Deleted: {
|
||||
Company: {
|
||||
Id: 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
Modified: {
|
||||
Company: {
|
||||
Id: 0,
|
||||
Content: {
|
||||
Original: 'before',
|
||||
Current: 'after'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,75 @@
|
|||
using VerifyXunit;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using VerifyXunit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
public class Tests :
|
||||
VerifyBase
|
||||
{
|
||||
#region Added
|
||||
[Fact]
|
||||
public async Task Added()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using var context = new SampleDbContext(options);
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
await Verify(context);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Deleted
|
||||
[Fact]
|
||||
public async Task Deleted()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
var company = context.Companies.Single();
|
||||
context.Companies.Remove(company);
|
||||
await Verify(context);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Modified
|
||||
[Fact]
|
||||
public async Task Modified()
|
||||
{
|
||||
var options = DbContextOptions();
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Add(new Company {Content = "before"});
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
await using (var context = new SampleDbContext(options))
|
||||
{
|
||||
context.Companies.Single().Content = "after";
|
||||
await Verify(context);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
static DbContextOptions<SampleDbContext> DbContextOptions(
|
||||
[CallerMemberName] string databaseName = "")
|
||||
{
|
||||
return new DbContextOptionsBuilder<SampleDbContext>()
|
||||
.UseInMemoryDatabase(databaseName)
|
||||
.Options;
|
||||
}
|
||||
|
||||
public Tests(ITestOutputHelper output) :
|
||||
base(output)
|
||||
{
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="ProjectDefaults" Version="1.0.40" PrivateAssets="All" />
|
||||
<ProjectReference Include="..\Verify.EntityFramework\Verify.EntityFramework.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Snippets\Usage.Test.verified.txt">
|
||||
<DependentUpon>Usage.cs</DependentUpon>
|
||||
</None>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -18,12 +18,64 @@ class DbContextConverter :
|
|||
writer.WriteStartObject();
|
||||
|
||||
var entries = context.ChangeTracker.Entries().ToList();
|
||||
HandleModified(entries,writer, serializer);
|
||||
HandleAdded(entries, writer, serializer);
|
||||
HandleModified(entries, writer, serializer);
|
||||
HandleDeleted(entries, writer, serializer);
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
void HandleModified(List<EntityEntry> entries,JsonWriter writer, JsonSerializer serializer)
|
||||
static void HandleDeleted(List<EntityEntry> entries,JsonWriter writer, JsonSerializer serializer)
|
||||
{
|
||||
var deleted = entries
|
||||
.Where(x => x.State == EntityState.Deleted)
|
||||
.ToList();
|
||||
if (!deleted.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
writer.WritePropertyName("Deleted");
|
||||
writer.WriteStartObject();
|
||||
foreach (var entry in deleted)
|
||||
{
|
||||
writer.WritePropertyName(entry.Metadata.DisplayName());
|
||||
writer.WriteStartObject();
|
||||
WriteId(writer, serializer, entry);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
static void HandleAdded(List<EntityEntry> entries, JsonWriter writer, JsonSerializer serializer)
|
||||
{
|
||||
var added = entries
|
||||
.Where(x => x.State == EntityState.Added)
|
||||
.ToList();
|
||||
if (!added.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WritePropertyName("Added");
|
||||
writer.WriteStartObject();
|
||||
foreach (var entry in added)
|
||||
{
|
||||
writer.WritePropertyName(entry.Metadata.DisplayName());
|
||||
writer.WriteStartObject();
|
||||
|
||||
foreach (var property in entry.Properties)
|
||||
{
|
||||
writer.WritePropertyName(property.Metadata.Name);
|
||||
serializer.Serialize(writer, property.OriginalValue);
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
static void HandleModified(List<EntityEntry> entries,JsonWriter writer, JsonSerializer serializer)
|
||||
{
|
||||
var modified = entries
|
||||
.Where(x => x.State == EntityState.Modified)
|
||||
|
@ -45,6 +97,8 @@ class DbContextConverter :
|
|||
{
|
||||
writer.WritePropertyName(entry.Metadata.DisplayName());
|
||||
writer.WriteStartObject();
|
||||
|
||||
WriteId(writer, serializer, entry);
|
||||
foreach (var property in entry.ChangedProperties())
|
||||
{
|
||||
writer.WritePropertyName(property.Metadata.Name);
|
||||
|
@ -58,4 +112,25 @@ class DbContextConverter :
|
|||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
|
||||
static void WriteId(JsonWriter writer, JsonSerializer serializer, EntityEntry entry)
|
||||
{
|
||||
var ids = entry.FindPrimaryKeyValues().ToList();
|
||||
if (!ids.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ids.Count == 1)
|
||||
{
|
||||
var (name, value) = ids.Single();
|
||||
writer.WritePropertyName(name);
|
||||
serializer.Serialize(writer, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePropertyName("Ids");
|
||||
serializer.Serialize(writer, ids);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,4 +10,14 @@ static class Extensions
|
|||
.Where(x => x.IsModified);
|
||||
}
|
||||
|
||||
public static IEnumerable<(string name, object value)> FindPrimaryKeyValues(this EntityEntry entry)
|
||||
{
|
||||
var primaryKey = entry.Metadata.FindPrimaryKey();
|
||||
foreach (var property in primaryKey.Properties)
|
||||
{
|
||||
var name = property.Name;
|
||||
var value = entry.Property(name).CurrentValue;
|
||||
yield return (name, value);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче