Skip to content

Commit

Permalink
SAVEPOINT
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisdoomen committed Mar 17, 2024
1 parent e969354 commit d62e2e8
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 27 deletions.
44 changes: 25 additions & 19 deletions Src/FluentAssertions/Execution/AssertionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public sealed class AssertionScope : IAssertionScope

private static readonly AsyncLocal<AssertionScope> CurrentScope = new();
private Func<string> callerIdentityProvider = () => CallerIdentifier.DetermineCallerIdentity();
#pragma warning disable CA2213
private AssertionScope parent;
#pragma warning restore CA2213
private Func<string> expectation;
private string fallbackIdentifier = "object";
private bool? succeeded;
Expand All @@ -48,24 +50,20 @@ public DeferredReportable(Func<string> valueFunc)
#endregion

/// <summary>
/// Starts a named scope within which multiple assertions can be executed
/// Starts an unnamed scope within which multiple assertions can be executed
/// and which will not throw until the scope is disposed.
/// </summary>
public AssertionScope(string context)
: this()
public AssertionScope()
: this(new CollectingAssertionStrategy())
{
if (!string.IsNullOrEmpty(context))
{
Context = new Lazy<string>(() => context);
}
}

/// <summary>
/// Starts an unnamed scope within which multiple assertions can be executed
/// Starts a named scope within which multiple assertions can be executed
/// and which will not throw until the scope is disposed.
/// </summary>
public AssertionScope()
: this(new CollectingAssertionStrategy())
public AssertionScope(string context)
: this(new CollectingAssertionStrategy(), new Lazy<string>(() => context))
{
}

Expand All @@ -75,42 +73,50 @@ public AssertionScope()
/// <param name="assertionStrategy">The assertion strategy for this scope.</param>
/// <exception cref="ArgumentNullException"><paramref name="assertionStrategy"/> is <see langword="null"/>.</exception>
public AssertionScope(IAssertionStrategy assertionStrategy)
: this(assertionStrategy, GetCurrentAssertionScope())
: this(assertionStrategy, context: null)
{
SetCurrentAssertionScope(this);
}

/// <summary>
/// Starts a named scope within which multiple assertions can be executed
/// and which will not throw until the scope is disposed.
/// </summary>
public AssertionScope(Lazy<string> context)
: this()
: this(new CollectingAssertionStrategy(), context)
{
Context = context;
}

/// <summary>
/// Starts a new scope based on the given assertion strategy and parent assertion scope
/// </summary>
/// <param name="assertionStrategy">The assertion strategy for this scope.</param>
/// <param name="parent">The parent assertion scope for this scope.</param>
/// <exception cref="ArgumentNullException"><paramref name="assertionStrategy"/> is <see langword="null"/>.</exception>
private AssertionScope(IAssertionStrategy assertionStrategy, AssertionScope parent)
private AssertionScope(IAssertionStrategy assertionStrategy, Lazy<string> context)
{
this.assertionStrategy = assertionStrategy
?? throw new ArgumentNullException(nameof(assertionStrategy));

this.parent = parent;
parent = GetCurrentAssertionScope();

if (parent is not null)
{
contextData.Add(parent.contextData);
Context = parent.Context;
reason = parent.reason;
callerIdentityProvider = parent.callerIdentityProvider;
FormattingOptions = parent.FormattingOptions.Clone();
Context = new Lazy<string>(() => JoinContext(parent.Context, context));
}
else
{
Context = context;
}

SetCurrentAssertionScope(this);
}

private static string JoinContext(params Lazy<string>[] contexts)
{
return string.Join("/", contexts.Where(ctx => ctx is not null).Select(x => x.Value));
}

/// <summary>
Expand All @@ -128,7 +134,7 @@ public static AssertionScope Current
#pragma warning disable CA2000 // AssertionScope should not be disposed here
get
{
return GetCurrentAssertionScope() ?? new AssertionScope(new DefaultAssertionStrategy(), parent: null);
return GetCurrentAssertionScope() ?? new AssertionScope(new DefaultAssertionStrategy());
}
#pragma warning restore CA2000
private set => SetCurrentAssertionScope(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,29 @@ public void Message_should_use_the_lazy_name_of_the_scope_as_context()
}

[Fact]
public void Message_should_contain_each_unique_failed_assertion_seperately()
public void Nested_scopes_will_use_the_name_of_their_outer_scope_as_context()
{
// Act
Action act = () =>
{
using var _ = new AssertionScope();
var values = new Dictionary<int, int>();
values.Should().ContainKey(0);
values.Should().ContainKey(1);
using var outerScope = new AssertionScope("outer");
using var innerScope = new AssertionScope("inner");
new[] { 1, 2, 3 }.Should().Equal(3, 2, 1);
};

// Assert
act.Should().Throw<XunitException>()
.WithMessage(
"Expected * to contain key 0.\n" +
"Expected * to contain key 1.\n");
.WithMessage("Expected outer/inner to be equal to*");
}

[Fact]
public void Message_should_contain_each_unique_failed_assertion_seperately()
{
// Act
using var _ = new AssertionScope();
var values = new Dictionary<int, int>();
values.Should().ContainKey(0);
values.Should().ContainKey(1);
}

[Fact]
Expand Down

0 comments on commit d62e2e8

Please sign in to comment.