Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lambda expressions instead of json path for ignore and accept #175

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,36 @@ Snapshot.Match<Person>(person, matchOptions => matchOptions.HashField("Thumbnail
Snapshot.Match<Person>(person, matchOptions => matchOptions.HashField("**.Data"));
```

### Assert Fields in Snapshots
#### Ignore using a lambda expression

We also support ignoring fields using lambda expressions instead of json paths.

```csharp
// Ignores the Id field of the person
Snapshot.Match<Person>(person, matchOptions => matchOptions.IgnoreField(person => person.Id));
```

### Assert the type of a field but ignore its value

Using the `.AcceptField()` and `.AcceptFields()` syntax we can assert that a certain field contains a value of a given type.
Unlike ignore the snapshot file will not contain the field value but instead the type assertion, for example: `"Field": "AcceptAny<String>"`.
This is desirable because the snapshot will not change when it is recreated at a later time.

#### Example with json path syntax

```csharp
// Assert that the id field is a guid and writes "AcceptAny<Guid>" into the snapshot file
Snapshot.Match<Person>(person, matchOptions => matchOptions.AcceptField<Guid>("id"));
```

#### Examle with lambda expressions

```csharp
// Assert that the id field is a guid and writes "AcceptAny<Guid>" into the snapshot file
Snapshot.Match<Person>(person, matchOptions => matchOptions.AcceptField(person => person.Id));
```

### Assert Fields in Snapshots Matches

Sometimes there are fields in a snapshot, which you want to assert separately against another value.

Expand Down
29 changes: 19 additions & 10 deletions src/Snapshooter.Json/Snapshot.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using Snapshooter.Core;
using Snapshooter.Core.Serialization;

Expand Down Expand Up @@ -26,11 +26,15 @@ public static class Snapshot
/// <param name="matchOptions">
/// Additional compare actions, which can be applied during the comparison
/// </param>
public static void Match<T>(T currentResult,
string snapshotName,
Func<MatchOptions, MatchOptions> matchOptions = null)
public static void Match<T>(
T currentResult,
string snapshotName,
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotName, matchOptions);
Match(
(object)currentResult,
snapshotName,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand All @@ -56,12 +60,17 @@ public static class Snapshot
/// <param name="matchOptions">
/// Additional compare actions, which can be applied during the comparison
/// </param>
public static void Match<T>(T currentResult,
string snapshotName,
SnapshotNameExtension snapshotNameExtension,
Func<MatchOptions, MatchOptions> matchOptions = null)
public static void Match<T>(
T currentResult,
string snapshotName,
SnapshotNameExtension snapshotNameExtension,
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotName, snapshotNameExtension, matchOptions);
Match(
(object)currentResult,
snapshotName,
snapshotNameExtension,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using FluentAssertions.Primitives;

// Extension of Fluent assertion to not loose type information of asserted object
namespace FluentAssertions;

/// <summary>
/// Contains a number of methods to assert that an <see cref="object"/> is in the expected state.
/// </summary>
public class TypedAssertions<T> : ObjectAssertions<T, TypedAssertions<T>>
{
internal TypedAssertions(T value) : base(value)
{
}
}

/// <summary>
/// Contains extension methods for custom assertions in unit tests.
/// </summary>
public static class AssertionExtensions
{
/// <summary>
/// Returns an <see cref="TypedAssertions{T}"/> object that can be used to assert the
/// current <see cref="object"/>.
/// </summary>
public static TypedAssertions<TSubject> Should<TSubject>(this TSubject actualValue)
{
return new TypedAssertions<TSubject>(actualValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using FluentAssertions;
using System;

namespace Snapshooter.NUnit;

public static class FluentSnapshotExtension
{
/// <summary>
/// Creates a json snapshot of the given object and compares it with the
/// already existing snapshot of the test.
/// If no snapshot exists, a new snapshot will be created from the current result
/// and saved under a certain file path, which will shown within the test message.
/// </summary>
/// <param name="currentResult">The object to match.</param>
/// <param name="matchOptions">
/// Additional compare actions, which can be applied during the snapshot comparison
/// </param>
public static void MatchSnapshot<TSubject>(
this TypedAssertions<TSubject> currentResult,
Func<MatchOptions<TSubject>, MatchOptions<TSubject>>? matchOptions = null)
{
var cleanedObject = currentResult.RemoveUnwantedWrappers();

Func<MatchOptions, MatchOptions>? chainedMatchOptions =
matchOptions != null ? m => matchOptions(new MatchOptions<TSubject>(m)) : null;

Snapshot.Match(
cleanedObject,
chainedMatchOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(CCResourceProjectProps)" Condition="Exists('$(CCResourceProjectProps)')" />

<PropertyGroup>
<AssemblyName>Snapshooter.NUnit.TypedFluentAssertions</AssemblyName>
<RootNamespace>Snapshooter.NUnit.TypedFluentAssertions</RootNamespace>
<PackageId>Snapshooter.NUnit.TypedFluentAssertions</PackageId>
<Description>
TypedFluentAssertions is an extention to FluentAssertions and Snapshooter.NUnit.
It allows to configure snapshots in a type save and fluent way.
</Description>
<IsTestProject>false</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Snapshooter.NUnit\Snapshooter.NUnit.csproj" />
</ItemGroup>

</Project>
37 changes: 26 additions & 11 deletions src/Snapshooter.NUnit/Snapshot.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using Snapshooter.Core;
using Snapshooter.Core.Serialization;
Expand Down Expand Up @@ -30,9 +30,11 @@ public static class Snapshot
/// </param>
public static void Match<T>(
T currentResult,
Func<MatchOptions, MatchOptions> matchOptions = null)
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, matchOptions);
Match(
(object)currentResult,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand All @@ -58,9 +60,12 @@ public static class Snapshot
public static void Match<T>(
T currentResult,
SnapshotNameExtension snapshotNameExtension,
Func<MatchOptions, MatchOptions> matchOptions = null)
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotNameExtension, matchOptions);
Match(
(object)currentResult,
snapshotNameExtension,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand All @@ -81,9 +86,12 @@ public static class Snapshot
public static void Match<T>(
T currentResult,
string snapshotName,
Func<MatchOptions, MatchOptions> matchOptions = null)
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotName, matchOptions);
Match(
(object)currentResult,
snapshotName,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand Down Expand Up @@ -114,9 +122,13 @@ public static class Snapshot
T currentResult,
string snapshotName,
SnapshotNameExtension snapshotNameExtension,
Func<MatchOptions, MatchOptions> matchOptions = null)
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotName, snapshotNameExtension, matchOptions);
Match(
(object)currentResult,
snapshotName,
snapshotNameExtension,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand All @@ -134,9 +146,12 @@ public static class Snapshot
public static void Match<T>(
T currentResult,
SnapshotFullName snapshotFullName,
Func<MatchOptions, MatchOptions> matchOptions = null)
Func<MatchOptions<T>, MatchOptions<T>> matchOptions = null)
{
Match((object)currentResult, snapshotFullName, matchOptions);
Match(
(object)currentResult,
snapshotFullName,
o => matchOptions == null ? o : matchOptions(new MatchOptions<T>(o)));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using FluentAssertions.Primitives;

// Extension of Fluent assertion to not loose type information of asserted object
namespace FluentAssertions;

/// <summary>
/// Contains a number of methods to assert that an <see cref="object"/> is in the expected state.
/// </summary>
public class TypedAssertions<T> : ObjectAssertions<T, TypedAssertions<T>>
{
internal TypedAssertions(T value) : base(value)
{
}
}

/// <summary>
/// Contains extension methods for custom assertions in unit tests.
/// </summary>
public static class AssertionExtensions
{
/// <summary>
/// Returns an <see cref="TypedAssertions{T}"/> object that can be used to assert the
/// current <see cref="object"/>.
/// </summary>
public static TypedAssertions<TSubject> Should<TSubject>(this TSubject actualValue)
{
return new TypedAssertions<TSubject>(actualValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using FluentAssertions;
using System;

namespace Snapshooter.Xunit;

public static class FluentSnapshotExtension
{
/// <summary>
/// Creates a json snapshot of the given object and compares it with the
/// already existing snapshot of the test.
/// If no snapshot exists, a new snapshot will be created from the current result
/// and saved under a certain file path, which will shown within the test message.
/// </summary>
/// <param name="currentResult">The object to match.</param>
/// <param name="matchOptions">
/// Additional compare actions, which can be applied during the snapshot comparison
/// </param>
public static void MatchSnapshot<TSubject>(
this TypedAssertions<TSubject> currentResult,
Func<MatchOptions<TSubject>, MatchOptions<TSubject>>? matchOptions = null)
{
var cleanedObject = currentResult.RemoveUnwantedWrappers();

Func<MatchOptions, MatchOptions>? chainedMatchOptions =
matchOptions != null ? m => matchOptions(new MatchOptions<TSubject>(m)) : null;

Snapshot.Match(
cleanedObject,
chainedMatchOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(CCResourceProjectProps)" Condition="Exists('$(CCResourceProjectProps)')" />

<PropertyGroup>
<AssemblyName>Snapshooter.Xunit.TypedFluentAssertions</AssemblyName>
<RootNamespace>Snapshooter.Xunit.TypedFluentAssertions</RootNamespace>
<PackageId>Snapshooter.Xunit.TypedFluentAssertions</PackageId>
<Description>
TypedFluentAssertions is an extention to FluentAssertions and Snapshooter.Xunit.
It allows to configure snapshots in a type save and fluent way.
</Description>
<IsTestProject>false</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Snapshooter.Xunit\Snapshooter.Xunit.csproj" />
</ItemGroup>

</Project>
Loading
Loading