diff --git a/Directory.Packages.props b/Directory.Packages.props index 65bbcc0..a7e0e91 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,8 +4,9 @@ - - + + + diff --git a/README.md b/README.md index edd7356..a5dcc32 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,39 @@ await That(mock.VerifyMock.Invoked.MyMethod()).AtMost(4.Times()); // At most 4 await That(mock.VerifyMock.Invoked.MyMethod()).Exactly(2.Times()); // Exactly 2 times ``` +#### Asynchronous verification + +With `Within(TimeSpan timeout)`, you can check whether the expected number of calls occurred within a given time +interval. This is useful for asynchronous or delayed invocations in the background. + +```csharp +var mock = Mock.Create(); + +// Start asynchronous calls, e.g., in a Task +Task.Run(async () => +{ + await Task.Delay(500); + mock.MyMethod(); +}); + +// Verifies that MyMethod was called at least once within 1 second +await That(mock.VerifyMock.Invoked.MyMethod()) + .AtLeastOnce() + .Within(TimeSpan.FromSeconds(1)); +``` + +Instead of a fixed time span, you can also provide a `CancellationToken` to limit how long the verification should wait +for the expected interactions: + +```csharp +var token = new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token; + +// Verifies that MyMethod was called at least once within 1 second +await That(mock.VerifyMock.Invoked.MyMethod()) + .AtLeastOnce() + .WithCancellation(token); +``` + ### Interaction order Verify that methods were called in a specific sequence: diff --git a/Source/aweXpect.Mockolate/Options/WithinOptions.cs b/Source/aweXpect.Mockolate/Options/WithinOptions.cs new file mode 100644 index 0000000..a95cec1 --- /dev/null +++ b/Source/aweXpect.Mockolate/Options/WithinOptions.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading; + +namespace aweXpect.Options; + +/// +/// The options for a verification result which allows specifying a timeout and/or cancellation token for the +/// verification. +/// +public class WithinOptions +{ + /// + /// The timeout that is applied to the verification. + /// + public TimeSpan? Timeout { get; set; } + + /// + /// The cancellation token that is used to cancel the verification. + /// + public CancellationToken? CancellationToken { get; set; } +} diff --git a/Source/aweXpect.Mockolate/Results/AndOrWithinResult.cs b/Source/aweXpect.Mockolate/Results/AndOrWithinResult.cs new file mode 100644 index 0000000..7b8948e --- /dev/null +++ b/Source/aweXpect.Mockolate/Results/AndOrWithinResult.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using aweXpect.Core; +using aweXpect.Options; + +namespace aweXpect.Results; + +/// +/// The result of a verification result which allows specifying a timeout and/or cancellation token for the +/// verification. +/// +/// +/// +/// +public class AndOrWithinResult( + ExpectationBuilder expectationBuilder, + TThat returnValue, + WithinOptions options) + : AndOrWithinResult>( + expectationBuilder, + returnValue, + options); + +/// +public class AndOrWithinResult : AndOrResult + where TSelf : AndOrWithinResult +{ + private readonly WithinOptions _options; + + /// + protected AndOrWithinResult(ExpectationBuilder expectationBuilder, TThat returnValue, WithinOptions options) + : base(expectationBuilder, returnValue) + { + _options = options; + } + + /// + /// …within the given . + /// + public TSelf Within(TimeSpan timeout) + { + _options.Timeout = timeout; + return (TSelf)this; + } + + /// + /// …with the given . + /// + public new TSelf WithCancellation(CancellationToken cancellationToken) + { + _options.CancellationToken = cancellationToken; + base.WithCancellation(cancellationToken); + return (TSelf)this; + } +} diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeast.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeast.cs index 2c4c361..664f8f2 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeast.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeast.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -8,11 +9,16 @@ namespace aweXpect; public static partial class ThatVerificationResult { /// - /// Verifies that the checked interaction happened at least the number of . + /// Verifies that the checked interaction happened at least the number of . /// - public static AndOrResult, IThat>> AtLeast( - this IThat> subject, Times times) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasAtLeastConstraint(expectationBuilder, it, grammars, times.Value)), - subject); + public static AndOrWithinResult, IThat>> + AtLeast(this IThat> subject, Times times) + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasAtLeastConstraint(expectationBuilder, it, grammars, times.Value, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastOnce.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastOnce.cs index 84b2aad..e26c7e7 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastOnce.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastOnce.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -10,9 +11,14 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened at least once. /// - public static AndOrResult, IThat>> AtLeastOnce( - this IThat> subject) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasAtLeastConstraint(expectationBuilder, it, grammars, 1)), - subject); + public static AndOrWithinResult, IThat>> + AtLeastOnce(this IThat> subject) + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasAtLeastConstraint(expectationBuilder, it, grammars, 1, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastTwice.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastTwice.cs index f70926e..a35151f 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastTwice.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtLeastTwice.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -10,9 +11,14 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened at least twice. /// - public static AndOrResult, IThat>> AtLeastTwice( - this IThat> subject) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasAtLeastConstraint(expectationBuilder, it, grammars, 2)), - subject); + public static AndOrWithinResult, IThat>> + AtLeastTwice(this IThat> subject) + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasAtLeastConstraint(expectationBuilder, it, grammars, 2, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMost.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMost.cs index 178a8d6..0c4e49d 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMost.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMost.cs @@ -8,10 +8,10 @@ namespace aweXpect; public static partial class ThatVerificationResult { /// - /// Verifies that the checked interaction happened at most the number of . + /// Verifies that the checked interaction happened at most the number of . /// - public static AndOrResult, IThat>> AtMost( - this IThat> subject, Times times) + public static AndOrResult, IThat>> + AtMost(this IThat> subject, Times times) => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) => new HasAtMostConstraint(expectationBuilder, it, grammars, times.Value)), subject); diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostOnce.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostOnce.cs index 8cf432e..09766ae 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostOnce.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostOnce.cs @@ -10,8 +10,8 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened at most once. /// - public static AndOrResult, IThat>> AtMostOnce( - this IThat> subject) + public static AndOrResult, IThat>> + AtMostOnce(this IThat> subject) => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) => new HasAtMostConstraint(expectationBuilder, it, grammars, 1)), subject); diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostTwice.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostTwice.cs index ff22e20..cf92019 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostTwice.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.AtMostTwice.cs @@ -10,8 +10,8 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened at most twice. /// - public static AndOrResult, IThat>> AtMostTwice( - this IThat> subject) + public static AndOrResult, IThat>> + AtMostTwice(this IThat> subject) => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) => new HasAtMostConstraint(expectationBuilder, it, grammars, 2)), subject); diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Between.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Between.cs index 5f5844d..66a0d51 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Between.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Between.cs @@ -1,8 +1,11 @@ using System.Diagnostics.CodeAnalysis; using System.Text; +using System.Threading; +using System.Threading.Tasks; using aweXpect.Core; using aweXpect.Core.Constraints; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -13,28 +16,69 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened between … /// - public static BetweenResult, IThat>>, Times> - Between( - this IThat> subject, int minimum) - => new(maximum => new AndOrResult, IThat>>(subject.Get() - .ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasBetweenConstraint(expectationBuilder, it, grammars, minimum, maximum.Value)), - subject)); - + public static BetweenResult, IThat>>, + Times> + Between(this IThat> subject, int minimum) + { + WithinOptions options = new(); + return new + BetweenResult, IThat>>, + Times>(maximum + => new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasBetweenConstraint(expectationBuilder, it, grammars, minimum, + maximum.Value, options)), + subject, + options)); + } + private sealed class HasBetweenConstraint( ExpectationBuilder expectationBuilder, string it, ExpectationGrammars grammars, int minimum, - int maximum) + int maximum, + WithinOptions options) : ConstraintResult.WithValue>(grammars), - IValueConstraint> + IAsyncConstraint> { private int _count = -1; private string _expectation = ""; - public ConstraintResult IsMetBy(VerificationResult actual) + public async Task IsMetBy(VerificationResult actual, + CancellationToken cancellationToken) { + if (options.CancellationToken is not null) + { + actual = actual.WithCancellation(options.CancellationToken.Value); + } + + if (options.Timeout is not null) + { + actual = actual.Within(options.Timeout.Value); + } + else if (expectationBuilder.Timeout is not null) + { + actual = actual.Within(expectationBuilder.Timeout.Value); + } + + if (actual is IAsyncVerificationResult asyncVerificationResult) + { + _expectation = asyncVerificationResult.Expectation; + Actual = actual; + Outcome = await asyncVerificationResult.VerifyAsync(interactions => + { + string context = Formatter.Format(interactions, FormattingOptions.MultipleLines); + expectationBuilder.UpdateContexts(contexts => contexts.Add( + new ResultContext.SyncCallback("Interactions", () => context))); + _count = interactions.Length; + return interactions.Length >= minimum && interactions.Length <= maximum; + }) + ? Outcome.Success + : Outcome.Failure; + return this; + } + IVerificationResult result = actual; _expectation = result.Expectation; Actual = actual; diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Exactly.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Exactly.cs index 97ae108..89d2c91 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Exactly.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Exactly.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -8,11 +9,16 @@ namespace aweXpect; public static partial class ThatVerificationResult { /// - /// Verifies that the checked interaction happened exactly the number of . + /// Verifies that the checked interaction happened exactly the number of . /// - public static AndOrResult, IThat>> Exactly( - this IThat> subject, Times times) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasExactlyConstraint(expectationBuilder, it, grammars, times.Value)), - subject); + public static AndOrWithinResult, IThat>> + Exactly(this IThat> subject, Times times) + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasExactlyConstraint(expectationBuilder, it, grammars, times.Value, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Never.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Never.cs index 16bf94d..22e265e 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Never.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Never.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -10,9 +11,13 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened never. /// - public static AndOrResult, IThat>> Never( - this IThat> subject) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasExactlyConstraint(expectationBuilder, it, grammars, 0)), + public static AndOrResult, IThat>> + Never(this IThat> subject) + { + WithinOptions options = new(); + return new AndOrResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasExactlyConstraint(expectationBuilder, it, grammars, 0, options)), subject); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Once.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Once.cs index a9013e6..b0fe63d 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Once.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Once.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -10,9 +11,14 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened exactly once. /// - public static AndOrResult, IThat>> Once( - this IThat> subject) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasExactlyConstraint(expectationBuilder, it, grammars, 1)), - subject); + public static AndOrWithinResult, IThat>> + Once(this IThat> subject) + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasExactlyConstraint(expectationBuilder, it, grammars, 1, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Times.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Times.cs index 08b068f..c4f9923 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Times.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Times.cs @@ -2,9 +2,12 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text; +using System.Threading; +using System.Threading.Tasks; using aweXpect.Core; using aweXpect.Core.Constraints; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -15,29 +18,67 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened according to the . /// - public static AndOrResult, IThat>> Times( + public static AndOrWithinResult, IThat>> Times( this IThat> subject, Func predicate, - [CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") - => new(subject.Get() - .ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new TimesConstraint(expectationBuilder, it, grammars, predicate, - doNotPopulateThisValue)), - subject); + [CallerArgumentExpression("predicate")] + string doNotPopulateThisValue = "") + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new TimesConstraint(expectationBuilder, it, grammars, predicate, doNotPopulateThisValue, + options)), + subject, + options); + } private sealed class TimesConstraint( ExpectationBuilder expectationBuilder, string it, ExpectationGrammars grammars, Func predicate, - string predicateExpression) + string predicateExpression, + WithinOptions options) : ConstraintResult.WithValue>(grammars), - IValueConstraint> + IAsyncConstraint> { private int _count = -1; private string _expectation = ""; - public ConstraintResult IsMetBy(VerificationResult actual) + public async Task IsMetBy(VerificationResult actual, + CancellationToken cancellationToken) { + if (options.CancellationToken is not null) + { + actual = actual.WithCancellation(options.CancellationToken.Value); + } + + if (options.Timeout is not null) + { + actual = actual.Within(options.Timeout.Value); + } + else if (expectationBuilder.Timeout is not null) + { + actual = actual.Within(expectationBuilder.Timeout.Value); + } + + if (actual is IAsyncVerificationResult asyncVerificationResult) + { + _expectation = asyncVerificationResult.Expectation; + Actual = actual; + Outcome = await asyncVerificationResult.VerifyAsync(interactions => + { + string context = Formatter.Format(interactions, FormattingOptions.MultipleLines); + expectationBuilder.UpdateContexts(contexts => contexts.Add( + new ResultContext.SyncCallback("Interactions", () => context))); + _count = interactions.Length; + return predicate(_count); + }) + ? Outcome.Success + : Outcome.Failure; + return this; + } + IVerificationResult result = actual; _expectation = result.Expectation; Actual = actual; diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.Twice.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.Twice.cs index cf4c8df..47f1629 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.Twice.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.Twice.cs @@ -1,5 +1,6 @@ using aweXpect.Core; using aweXpect.Helpers; +using aweXpect.Options; using aweXpect.Results; using Mockolate.Verify; @@ -10,9 +11,14 @@ public static partial class ThatVerificationResult /// /// Verifies that the checked interaction happened exactly twice. /// - public static AndOrResult, IThat>> Twice( + public static AndOrWithinResult, IThat>> Twice( this IThat> subject) - => new(subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) - => new HasExactlyConstraint(expectationBuilder, it, grammars, 2)), - subject); + { + WithinOptions options = new(); + return new AndOrWithinResult, IThat>>( + subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars) + => new HasExactlyConstraint(expectationBuilder, it, grammars, 2, options)), + subject, + options); + } } diff --git a/Source/aweXpect.Mockolate/ThatVerificationResult.cs b/Source/aweXpect.Mockolate/ThatVerificationResult.cs index be3983a..c47b42e 100644 --- a/Source/aweXpect.Mockolate/ThatVerificationResult.cs +++ b/Source/aweXpect.Mockolate/ThatVerificationResult.cs @@ -1,8 +1,11 @@ using System.Diagnostics.CodeAnalysis; using System.Text; +using System.Threading; +using System.Threading.Tasks; using aweXpect.Core; using aweXpect.Core.Constraints; using aweXpect.Helpers; +using aweXpect.Options; using Mockolate.Verify; namespace aweXpect; @@ -12,19 +15,61 @@ namespace aweXpect; /// public static partial class ThatVerificationResult { + private static string ToAmountString(this int number) + => number switch + { + 0 => "never", + 1 => "once", + 2 => "twice", + _ => $"{number} times", + }; + private sealed class HasExactlyConstraint( ExpectationBuilder expectationBuilder, string it, ExpectationGrammars grammars, - int expected) + int expected, + WithinOptions options) : ConstraintResult.WithValue>(grammars), - IValueConstraint> + IAsyncConstraint> { private int _count = -1; private string _expectation = ""; - public ConstraintResult IsMetBy(VerificationResult actual) + public async Task IsMetBy(VerificationResult actual, + CancellationToken cancellationToken) { + if (options.CancellationToken is not null) + { + actual = actual.WithCancellation(options.CancellationToken.Value); + } + + if (options.Timeout is not null) + { + actual = actual.Within(options.Timeout.Value); + } + else if (expectationBuilder.Timeout is not null) + { + actual = actual.Within(expectationBuilder.Timeout.Value); + } + + if (actual is IAsyncVerificationResult asyncVerificationResult) + { + _expectation = asyncVerificationResult.Expectation; + Actual = actual; + Outcome = await asyncVerificationResult.VerifyAsync(interactions => + { + string context = Formatter.Format(interactions, FormattingOptions.MultipleLines); + expectationBuilder.UpdateContexts(contexts => contexts.Add( + new ResultContext.SyncCallback("Interactions", () => context))); + _count = interactions.Length; + return interactions.Length == expected; + }) + ? Outcome.Success + : Outcome.Failure; + return this; + } + IVerificationResult result = actual; _expectation = result.Expectation; Actual = actual; @@ -61,7 +106,8 @@ protected override void AppendNormalResult(StringBuilder stringBuilder, string? } else { - stringBuilder.Append("found ").Append(it).Append(_count < expected ? " only " : " ").Append(_count.ToAmountString()); + stringBuilder.Append("found ").Append(it).Append(_count < expected ? " only " : " ") + .Append(_count.ToAmountString()); } } @@ -83,7 +129,7 @@ protected override void AppendNegatedResult(StringBuilder stringBuilder, string? public override bool TryGetValue([NotNullWhen(true)] out TValue? value) where TValue : default { if (typeof(TValue) == typeof(IDescribableSubject) && - new MyDescribableSubject() is TValue describableSubject) + new MyDescribableSubject() is TValue describableSubject) { value = describableSubject; return true; @@ -123,19 +169,13 @@ public ConstraintResult IsMetBy(VerificationResult actual) } protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append(_expectation).Append(" at most ").Append(expected.ToAmountString()); - } + => stringBuilder.Append(_expectation).Append(" at most ").Append(expected.ToAmountString()); protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append("found ").Append(it).Append(' ').Append(_count.ToAmountString()); - } + => stringBuilder.Append("found ").Append(it).Append(' ').Append(_count.ToAmountString()); protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append(_expectation).Append(" more than ").Append(expected.ToAmountString()); - } + => stringBuilder.Append(_expectation).Append(" more than ").Append(expected.ToAmountString()); protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null) { @@ -152,7 +192,7 @@ protected override void AppendNegatedResult(StringBuilder stringBuilder, string? public override bool TryGetValue([NotNullWhen(true)] out TValue? value) where TValue : default { if (typeof(TValue) == typeof(IDescribableSubject) && - new MyDescribableSubject() is TValue describableSubject) + new MyDescribableSubject() is TValue describableSubject) { value = describableSubject; return true; @@ -166,15 +206,48 @@ private sealed class HasAtLeastConstraint( ExpectationBuilder expectationBuilder, string it, ExpectationGrammars grammars, - int expected) + int expected, + WithinOptions options) : ConstraintResult.WithValue>(grammars), - IValueConstraint> + IAsyncConstraint> { private int _count = -1; private string _expectation = ""; - public ConstraintResult IsMetBy(VerificationResult actual) + public async Task IsMetBy(VerificationResult actual, + CancellationToken cancellationToken) { + if (options.CancellationToken is not null) + { + actual = actual.WithCancellation(options.CancellationToken.Value); + } + + if (options.Timeout is not null) + { + actual = actual.Within(options.Timeout.Value); + } + else if (expectationBuilder.Timeout is not null) + { + actual = actual.Within(expectationBuilder.Timeout.Value); + } + + if (actual is IAsyncVerificationResult asyncVerificationResult) + { + _expectation = asyncVerificationResult.Expectation; + Actual = actual; + Outcome = await asyncVerificationResult.VerifyAsync(interactions => + { + string context = Formatter.Format(interactions, FormattingOptions.MultipleLines); + expectationBuilder.UpdateContexts(contexts => contexts.Add( + new ResultContext.SyncCallback("Interactions", () => context))); + _count = interactions.Length; + return interactions.Length >= expected; + }) + ? Outcome.Success + : Outcome.Failure; + return this; + } + IVerificationResult result = actual; _expectation = result.Expectation; Actual = actual; @@ -192,9 +265,7 @@ public ConstraintResult IsMetBy(VerificationResult actual) } protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append(_expectation).Append(" at least ").Append(expected.ToAmountString()); - } + => stringBuilder.Append(_expectation).Append(" at least ").Append(expected.ToAmountString()); protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null) { @@ -209,19 +280,15 @@ protected override void AppendNormalResult(StringBuilder stringBuilder, string? } protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append(_expectation).Append(" less than ").Append(expected.ToAmountString()); - } + => stringBuilder.Append(_expectation).Append(" less than ").Append(expected.ToAmountString()); protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null) - { - stringBuilder.Append("found ").Append(it).Append(' ').Append(_count.ToAmountString()); - } + => stringBuilder.Append("found ").Append(it).Append(' ').Append(_count.ToAmountString()); public override bool TryGetValue([NotNullWhen(true)] out TValue? value) where TValue : default { if (typeof(TValue) == typeof(IDescribableSubject) && - new MyDescribableSubject() is TValue describableSubject) + new MyDescribableSubject() is TValue describableSubject) { value = describableSubject; return true; @@ -230,13 +297,4 @@ protected override void AppendNegatedResult(StringBuilder stringBuilder, string? return base.TryGetValue(out value); } } - - private static string ToAmountString(this int number) - => number switch - { - 0 => "never", - 1 => "once", - 2 => "twice", - _ => $"{number} times" - }; } diff --git a/Tests/Directory.Build.props b/Tests/Directory.Build.props index 31fac0d..2bad6a7 100644 --- a/Tests/Directory.Build.props +++ b/Tests/Directory.Build.props @@ -15,7 +15,7 @@ false true False - 701;1702;CA1845 + 701;1702;CA1845;xUnit1051 @@ -44,6 +44,7 @@ all + diff --git a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net10.0.txt b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net10.0.txt index cf47744..62da815 100644 --- a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net10.0.txt +++ b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net10.0.txt @@ -16,6 +16,29 @@ namespace Mockolate.Web } } } +namespace aweXpect.Options +{ + public class WithinOptions + { + public WithinOptions() { } + public System.Threading.CancellationToken? CancellationToken { get; set; } + public System.TimeSpan? Timeout { get; set; } + } +} +namespace aweXpect.Results +{ + public class AndOrWithinResult : aweXpect.Results.AndOrWithinResult> + { + public AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + } + public class AndOrWithinResult : aweXpect.Results.AndOrResult + where TSelf : aweXpect.Results.AndOrWithinResult + { + protected AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + public TSelf WithCancellation(System.Threading.CancellationToken cancellationToken) { } + public TSelf Within(System.TimeSpan timeout) { } + } +} namespace aweXpect { public static class ThatMockVerify @@ -25,18 +48,18 @@ namespace aweXpect } public static class ThatVerificationResult { - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMost(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostOnce(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostTwice(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Never(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Then(this aweXpect.Core.IThat> subject, params System.Func, Mockolate.Verify.VerificationResult>[] interactions) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } } } \ No newline at end of file diff --git a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net8.0.txt b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net8.0.txt index 16772e8..eaccb70 100644 --- a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net8.0.txt +++ b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_net8.0.txt @@ -16,6 +16,29 @@ namespace Mockolate.Web } } } +namespace aweXpect.Options +{ + public class WithinOptions + { + public WithinOptions() { } + public System.Threading.CancellationToken? CancellationToken { get; set; } + public System.TimeSpan? Timeout { get; set; } + } +} +namespace aweXpect.Results +{ + public class AndOrWithinResult : aweXpect.Results.AndOrWithinResult> + { + public AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + } + public class AndOrWithinResult : aweXpect.Results.AndOrResult + where TSelf : aweXpect.Results.AndOrWithinResult + { + protected AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + public TSelf WithCancellation(System.Threading.CancellationToken cancellationToken) { } + public TSelf Within(System.TimeSpan timeout) { } + } +} namespace aweXpect { public static class ThatMockVerify @@ -25,18 +48,18 @@ namespace aweXpect } public static class ThatVerificationResult { - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMost(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostOnce(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostTwice(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Never(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Then(this aweXpect.Core.IThat> subject, params System.Func, Mockolate.Verify.VerificationResult>[] interactions) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } } } \ No newline at end of file diff --git a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_netstandard2.0.txt b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_netstandard2.0.txt index 44bdb33..a22b11a 100644 --- a/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_netstandard2.0.txt +++ b/Tests/aweXpect.Mockolate.Api.Tests/Expected/aweXpect.Mockolate_netstandard2.0.txt @@ -1,5 +1,28 @@ [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/aweXpect/aweXpect.Mockolate.git")] [assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")] +namespace aweXpect.Options +{ + public class WithinOptions + { + public WithinOptions() { } + public System.Threading.CancellationToken? CancellationToken { get; set; } + public System.TimeSpan? Timeout { get; set; } + } +} +namespace aweXpect.Results +{ + public class AndOrWithinResult : aweXpect.Results.AndOrWithinResult> + { + public AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + } + public class AndOrWithinResult : aweXpect.Results.AndOrResult + where TSelf : aweXpect.Results.AndOrWithinResult + { + protected AndOrWithinResult(aweXpect.Core.ExpectationBuilder expectationBuilder, TThat returnValue, aweXpect.Options.WithinOptions options) { } + public TSelf WithCancellation(System.Threading.CancellationToken cancellationToken) { } + public TSelf Within(System.TimeSpan timeout) { } + } +} namespace aweXpect { public static class ThatMockVerify @@ -9,18 +32,18 @@ namespace aweXpect } public static class ThatVerificationResult { - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeast(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastOnce(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> AtLeastTwice(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMost(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostOnce(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> AtMostTwice(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } + public static aweXpect.Results.BetweenResult, aweXpect.Core.IThat>>, aweXpect.Core.Times> Between(this aweXpect.Core.IThat> subject, int minimum) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Exactly(this aweXpect.Core.IThat> subject, aweXpect.Core.Times times) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Never(this aweXpect.Core.IThat> subject) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Once(this aweXpect.Core.IThat> subject) { } public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Then(this aweXpect.Core.IThat> subject, params System.Func, Mockolate.Verify.VerificationResult>[] interactions) { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } - public static aweXpect.Results.AndOrResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Times(this aweXpect.Core.IThat> subject, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } + public static aweXpect.Results.AndOrWithinResult, aweXpect.Core.IThat>> Twice(this aweXpect.Core.IThat> subject) { } } } \ No newline at end of file diff --git a/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllInteractionsAreVerifiedTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllInteractionsAreVerifiedTests.cs index 3c08656..bfd0d6e 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllInteractionsAreVerifiedTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllInteractionsAreVerifiedTests.cs @@ -9,7 +9,7 @@ public sealed partial class ThatMockVerifyIs public sealed class AllInteractionsAreVerifiedTests { [Fact] - public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() + public async Task Negated_WhenAllAreVerified_ShouldThrow() { IMyService mock = Mock.Create(); @@ -20,34 +20,33 @@ public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() async Task Act() { - await That(mock.VerifyMock).AllInteractionsAreVerified(); + await That(mock.VerifyMock).DoesNotComplyWith(it => it.AllInteractionsAreVerified()); } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatMockVerifyIs.IMyService mock + has not all interactions verified, + but all were + """); } [Fact] - public async Task WhenOneInvocationIsNotVerified_ShouldThrow() + public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() { IMyService mock = Mock.Create(); mock.DoWork(1); mock.DoWork(2); - mock.VerifyMock.Invoked.DoWork(It.Is(1)).AtLeastOnce(); + mock.VerifyMock.Invoked.DoWork(It.IsAny()).AtLeastOnce(); async Task Act() { await That(mock.VerifyMock).AllInteractionsAreVerified(); } - await That(Act).Throws() - .WithMessage(""" - Expected that the ThatMockVerifyIs.IMyService mock - has all interactions verified, - but the following interaction was not verified: - - [1] invoke method aweXpect.Mockolate.Tests.ThatMockVerifyIs.IMyService.DoWork(2) - """); + await That(Act).DoesNotThrow(); } [Fact] @@ -77,25 +76,26 @@ Expected that the ThatMockVerifyIs.IMyService mock } [Fact] - public async Task Negated_WhenAllAreVerified_ShouldThrow() + public async Task WhenOneInvocationIsNotVerified_ShouldThrow() { IMyService mock = Mock.Create(); mock.DoWork(1); mock.DoWork(2); - mock.VerifyMock.Invoked.DoWork(It.IsAny()).AtLeastOnce(); + mock.VerifyMock.Invoked.DoWork(It.Is(1)).AtLeastOnce(); async Task Act() { - await That(mock.VerifyMock).DoesNotComplyWith(it => it.AllInteractionsAreVerified()); + await That(mock.VerifyMock).AllInteractionsAreVerified(); } await That(Act).Throws() .WithMessage(""" Expected that the ThatMockVerifyIs.IMyService mock - has not all interactions verified, - but all were + has all interactions verified, + but the following interaction was not verified: + - [1] invoke method aweXpect.Mockolate.Tests.ThatMockVerifyIs.IMyService.DoWork(2) """); } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllSetupsAreUsedTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllSetupsAreUsedTests.cs index a1425e0..7adfa70 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllSetupsAreUsedTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatMockVerifyIs.AllSetupsAreUsedTests.cs @@ -1,5 +1,4 @@ using Mockolate; -using Mockolate.Verify; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -9,7 +8,7 @@ public sealed partial class ThatMockVerifyIs public sealed class AllSetupsAreUsedTests { [Fact] - public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() + public async Task Negated_WhenAllSetupsAreUsed_ShouldThrow() { IMyService mock = Mock.Create(); mock.SetupMock.Method.DoWork(It.IsAny()); @@ -19,33 +18,32 @@ public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() async Task Act() { - await That(mock.VerifyMock).AllSetupsAreUsed(); + await That(mock.VerifyMock).DoesNotComplyWith(it => it.AllSetupsAreUsed()); } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatMockVerifyIs.IMyService mock + has not used all setups, + but all were + """); } [Fact] - public async Task WhenOneSetupIsNotUsed_ShouldThrow() + public async Task WhenAllInvocationsWereVerified_ShouldNotThrow() { IMyService mock = Mock.Create(); - mock.SetupMock.Method.DoWork(It.Is(1)); - mock.SetupMock.Method.DoWork(It.Is(2)); + mock.SetupMock.Method.DoWork(It.IsAny()); mock.DoWork(1); + mock.DoWork(2); async Task Act() { await That(mock.VerifyMock).AllSetupsAreUsed(); } - await That(Act).Throws() - .WithMessage(""" - Expected that the ThatMockVerifyIs.IMyService mock - has used all setups, - but the following setup was not used: - - void aweXpect.Mockolate.Tests.ThatMockVerifyIs.IMyService.DoWork(2) - """); + await That(Act).DoesNotThrow(); } [Fact] @@ -74,24 +72,25 @@ Expected that the ThatMockVerifyIs.IMyService mock } [Fact] - public async Task Negated_WhenAllSetupsAreUsed_ShouldThrow() + public async Task WhenOneSetupIsNotUsed_ShouldThrow() { IMyService mock = Mock.Create(); - mock.SetupMock.Method.DoWork(It.IsAny()); + mock.SetupMock.Method.DoWork(It.Is(1)); + mock.SetupMock.Method.DoWork(It.Is(2)); mock.DoWork(1); - mock.DoWork(2); async Task Act() { - await That(mock.VerifyMock).DoesNotComplyWith(it => it.AllSetupsAreUsed()); + await That(mock.VerifyMock).AllSetupsAreUsed(); } await That(Act).Throws() .WithMessage(""" Expected that the ThatMockVerifyIs.IMyService mock - has not used all setups, - but all were + has used all setups, + but the following setup was not used: + - void aweXpect.Mockolate.Tests.ThatMockVerifyIs.IMyService.DoWork(2) """); } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastOnceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastOnceTests.cs index 572559f..5974051 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastOnceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastOnceTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,30 +10,123 @@ public sealed partial class ThatVerificationResultIs public sealed class AtLeastOnceTests { [Fact] - public async Task WhenInvokedTwice_ShouldSucceed() + public async Task WhenInvokedInBackground_ShouldFail() { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least once, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(1)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce().WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(1)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce().Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(1)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce() + .WithTimeout(30.Seconds()); + + await backgroundTask; } [Fact] public async Task WhenInvokedMoreThanTwice_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + } await That(Act).DoesNotThrow(); } @@ -39,31 +134,51 @@ async Task Act() [Fact] public async Task WhenInvokedNever_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at least once, - but never found it - - Interactions: - [] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least once, + but never found it + + Interactions: + [] + """); } [Fact] public async Task WhenInvokedOnce_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + } + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenInvokedTwice_ShouldSucceed() + { + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastOnce(); + } await That(Act).DoesNotThrow(); } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTests.cs index b1baf59..560bc21 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,25 +10,24 @@ public sealed partial class ThatVerificationResultIs public sealed class AtLeastTests { [Theory] - [InlineData(0, false)] - [InlineData(3, true)] - [InlineData(8, true)] - public async Task WhenInvokedNever_ShouldFailUnlessZero(int times, bool shouldThrow) + [InlineData(3)] + [InlineData(6)] + [InlineData(18)] + public async Task WhenInvokedAtLeastTheSameTimes_ShouldSucceed(int times) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + + for (int i = 0; i < times; i++) + { + mock.MyMethod(1, false); + } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + } - await That(Act).Throws().OnlyIf(shouldThrow) - .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at least {times} times, - but never found it - - Interactions: - [] - """); + await That(Act).DoesNotThrow(); } [Theory] @@ -34,27 +35,134 @@ but never found it [InlineData(8, 6)] public async Task WhenInvokedFewerTimes_ShouldFail(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + } await That(Act).Throws() .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at least {times} times, - but found it only {invocationTimes} times - - Interactions: - [ - * - ] - """).AsWildcard(); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least {times} times, + but found it only {invocationTimes} times + + Interactions: + [ + * + ] + """).AsWildcard(); + } + + [Fact] + public async Task WhenInvokedInBackground_ShouldFail() + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(3); + } + + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least 3 times, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(3, 7)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times).WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 7)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times).Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 7)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times) + .WithTimeout(30.Seconds()); + + await backgroundTask; } [Theory] @@ -62,36 +170,43 @@ but found it only {invocationTimes} times [InlineData(6, 8)] public async Task WhenInvokedMoreOften_ShouldSucceed(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + } await That(Act).DoesNotThrow(); } [Theory] - [InlineData(3)] - [InlineData(6)] - [InlineData(18)] - public async Task WhenInvokedAtLeastTheSameTimes_ShouldSucceed(int times) + [InlineData(0, false)] + [InlineData(3, true)] + [InlineData(8, true)] + public async Task WhenInvokedNever_ShouldFailUnlessZero(int times, bool shouldThrow) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < times; i++) + async Task Act() { - mock.MyMethod(1, false); + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); } - async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeast(times); + await That(Act).Throws().OnlyIf(shouldThrow) + .WithMessage($""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least {times} times, + but never found it - await That(Act).DoesNotThrow(); + Interactions: + [] + """); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTwiceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTwiceTests.cs index 981a86f..99454ef 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTwiceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtLeastTwiceTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,29 +10,123 @@ public sealed partial class ThatVerificationResultIs public sealed class AtLeastTwiceTests { [Fact] - public async Task WhenInvokedTwice_ShouldSucceed() + public async Task WhenInvokedInBackground_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; - mock.MyMethod(1, false); - mock.MyMethod(1, false); + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least twice, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(2)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice().WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(2)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice().Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(7)] + [InlineData(2)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice() + .WithTimeout(30.Seconds()); + + await backgroundTask; } + [Fact] public async Task WhenInvokedMoreThanTwice_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + } await That(Act).DoesNotThrow(); } @@ -38,43 +134,63 @@ async Task Act() [Fact] public async Task WhenInvokedNever_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at least twice, - but never found it - - Interactions: - [] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least twice, + but never found it + + Interactions: + [] + """); } [Fact] public async Task WhenInvokedOnce_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at least twice, - but found it only once - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at least twice, + but found it only once + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); + } + + [Fact] + public async Task WhenInvokedTwice_ShouldSucceed() + { + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtLeastTwice(); + } + + await That(Act).DoesNotThrow(); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostOnceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostOnceTests.cs index 03e33fb..64e8618 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostOnceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostOnceTests.cs @@ -8,78 +8,87 @@ public sealed partial class ThatVerificationResultIs public sealed class AtMostOnceTests { [Fact] - public async Task WhenInvokedTwice_ShouldFail() + public async Task WhenInvokedMoreThanTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at most once, - but found it twice - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at most once, + but found it 3 times + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } + [Fact] - public async Task WhenInvokedMoreThanTwice_ShouldFail() + public async Task WhenInvokedNever_ShouldSucceed() { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); - mock.MyMethod(1, false); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + } - await That(Act).Throws() - .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at most once, - but found it 3 times - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + await That(Act).DoesNotThrow(); } [Fact] - public async Task WhenInvokedNever_ShouldSucceed() + public async Task WhenInvokedOnce_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + } await That(Act).DoesNotThrow(); } [Fact] - public async Task WhenInvokedOnce_ShouldSucceed() + public async Task WhenInvokedTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostOnce(); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at most once, + but found it twice + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTests.cs index 251299c..3a1b3f9 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTests.cs @@ -8,69 +8,75 @@ public sealed partial class ThatVerificationResultIs public sealed class AtMost { [Theory] - [InlineData(2, 0)] - [InlineData(4, 3)] - [InlineData(8, 6)] - public async Task WhenInvokedFewerTimes_ShouldSucceed(int times, int invocationTimes) + [InlineData(3)] + [InlineData(6)] + [InlineData(18)] + public async Task WhenInvokedAtMostTheSameTimes_ShouldSucceed(int times) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < times; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + } await That(Act).DoesNotThrow(); } [Theory] - [InlineData(3, 4)] - [InlineData(6, 8)] - public async Task WhenInvokedMoreOften_ShouldFail(int times, int invocationTimes) + [InlineData(2, 0)] + [InlineData(4, 3)] + [InlineData(8, 6)] + public async Task WhenInvokedFewerTimes_ShouldSucceed(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + } - await That(Act).Throws() - .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at most {times} times, - but found it {invocationTimes} times - - Interactions: - [ - * - ] - """).AsWildcard(); + await That(Act).DoesNotThrow(); } [Theory] - [InlineData(3)] - [InlineData(6)] - [InlineData(18)] - public async Task WhenInvokedAtMostTheSameTimes_ShouldSucceed(int times) + [InlineData(3, 4)] + [InlineData(6, 8)] + public async Task WhenInvokedMoreOften_ShouldFail(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < times; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMost(times); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage($""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at most {times} times, + but found it {invocationTimes} times + + Interactions: + [ + * + ] + """).AsWildcard(); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTwiceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTwiceTests.cs index 402de88..5b20a0c 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTwiceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.AtMostTwiceTests.cs @@ -7,53 +7,44 @@ public sealed partial class ThatVerificationResultIs { public sealed class AtMostTwiceTests { - [Fact] - public async Task WhenInvokedTwice_ShouldSucceed() - { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); - - async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); - - await That(Act).DoesNotThrow(); - } [Fact] public async Task WhenInvokedMoreThanTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) at most twice, - but found it 3 times - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) at most twice, + but found it 3 times + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } [Fact] public async Task WhenInvokedNever_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + } await That(Act).DoesNotThrow(); } @@ -61,12 +52,30 @@ async Task Act() [Fact] public async Task WhenInvokedOnce_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + } + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenInvokedTwice_ShouldSucceed() + { + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).AtMostTwice(); + } await That(Act).DoesNotThrow(); } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.BetweenTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.BetweenTests.cs index 400a245..af36185 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.BetweenTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.BetweenTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -7,6 +9,118 @@ public sealed partial class ThatVerificationResultIs { public sealed class BetweenTests { + [Fact] + public async Task WhenInvokedInBackground_ShouldFail() + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Between(3).And(6); + } + + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) between 3 and 6 times, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(3, 8, 7)] + [InlineData(5, 8, 5)] + [InlineData(5, 8, 8)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int minimum, int maximum, + int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Between(minimum).And(maximum) + .WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 8, 7)] + [InlineData(5, 8, 5)] + [InlineData(5, 8, 8)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int minimum, int maximum, int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Between(minimum).And(maximum) + .Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 8, 7)] + [InlineData(5, 8, 5)] + [InlineData(5, 8, 8)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int minimum, int maximum, + int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Between(minimum).And(maximum) + .WithTimeout(30.Seconds()); + + await backgroundTask; + } + [Theory] [InlineData(3, 5, 3)] [InlineData(3, 5, 5)] diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ExactlyTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ExactlyTests.cs index e0f0719..c3ceba6 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ExactlyTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ExactlyTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,25 +10,24 @@ public sealed partial class ThatVerificationResultIs public sealed class ExactlyTests { [Theory] - [InlineData(0, false)] - [InlineData(3, true)] - [InlineData(8, true)] - public async Task WhenInvokedNever_ShouldFailUnlessZero(int times, bool shouldThrow) + [InlineData(3)] + [InlineData(6)] + [InlineData(18)] + public async Task WhenInvokedExactlyTheSameTimes_ShouldSucceed(int times) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + + for (int i = 0; i < times; i++) + { + mock.MyMethod(1, false); + } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + } - await That(Act).Throws().OnlyIf(shouldThrow) - .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly {times} times, - but never found it - - Interactions: - [] - """); + await That(Act).DoesNotThrow(); } [Theory] @@ -34,27 +35,134 @@ but never found it [InlineData(8, 6)] public async Task WhenInvokedFewerTimes_ShouldFail(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + } await That(Act).Throws() .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly {times} times, - but found it only {invocationTimes} times - - Interactions: - [ - * - ] - """).AsWildcard(); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly {times} times, + but found it only {invocationTimes} times + + Interactions: + [ + * + ] + """).AsWildcard(); + } + + [Fact] + public async Task WhenInvokedInBackground_ShouldFail() + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(3); + } + + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly 3 times, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(3, 3)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times).WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 3)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times).Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(3, 3)] + [InlineData(5, 5)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int times, int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times) + .WithTimeout(30.Seconds()); + + await backgroundTask; } [Theory] @@ -62,46 +170,53 @@ but found it only {invocationTimes} times [InlineData(6, 8)] public async Task WhenInvokedMoreOften_ShouldFail(int times, int invocationTimes) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < invocationTimes; i++) + for (int i = 0; i < invocationTimes; i++) { mock.MyMethod(1, false); } async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + } await That(Act).Throws() .WithMessage($""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly {times} times, - but found it {invocationTimes} times - - Interactions: - [ - * - ] - """).AsWildcard(); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly {times} times, + but found it {invocationTimes} times + + Interactions: + [ + * + ] + """).AsWildcard(); } [Theory] - [InlineData(3)] - [InlineData(6)] - [InlineData(18)] - public async Task WhenInvokedExactlyTheSameTimes_ShouldSucceed(int times) + [InlineData(0, false)] + [InlineData(3, true)] + [InlineData(8, true)] + public async Task WhenInvokedNever_ShouldFailUnlessZero(int times, bool shouldThrow) { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); - for (var i = 0; i < times; i++) + async Task Act() { - mock.MyMethod(1, false); + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); } - async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Exactly(times); + await That(Act).Throws().OnlyIf(shouldThrow) + .WithMessage($""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly {times} times, + but never found it - await That(Act).DoesNotThrow(); + Interactions: + [] + """); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.NeverTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.NeverTests.cs index 4a84221..48a413f 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.NeverTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.NeverTests.cs @@ -8,88 +8,97 @@ public sealed partial class ThatVerificationResultIs public sealed class NeverTests { [Fact] - public async Task WhenInvokedTwice_ShouldFail() + public async Task WhenInvokedMoreThanTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - never invoked method MyMethod(1, false), - but found it twice - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + never invoked method MyMethod(1, false), + but found it 3 times + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } + [Fact] - public async Task WhenInvokedMoreThanTwice_ShouldFail() + public async Task WhenInvokedNever_ShouldSucceed() { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); - mock.MyMethod(1, false); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + } - await That(Act).Throws() - .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - never invoked method MyMethod(1, false), - but found it 3 times - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + await That(Act).DoesNotThrow(); } [Fact] public async Task WhenInvokedOnce_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - never invoked method MyMethod(1, false), - but found it once - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + never invoked method MyMethod(1, false), + but found it once + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } [Fact] - public async Task WhenInvokedNever_ShouldSucceed() + public async Task WhenInvokedTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Never(); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + never invoked method MyMethod(1, false), + but found it twice + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.OnceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.OnceTests.cs index 47047b6..fc2cec7 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.OnceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.OnceTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,87 +10,197 @@ public sealed partial class ThatVerificationResultIs public sealed class OnceTests { [Fact] - public async Task WhenInvokedTwice_ShouldFail() + public async Task WhenInvokedInBackground_ShouldFail() { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly once, - but found it twice - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly once, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(1)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once().WithCancellation(token); + + await backgroundTask; } + + [Theory] + [InlineData(1)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once().Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(1)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once().WithTimeout(30.Seconds()); + + await backgroundTask; + } + [Fact] public async Task WhenInvokedMoreThanTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly once, - but found it 3 times - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly once, + but found it 3 times + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } [Fact] public async Task WhenInvokedNever_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly once, - but never found it - - Interactions: - [] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly once, + but never found it + + Interactions: + [] + """); } [Fact] public async Task WhenInvokedOnce_ShouldSucceed() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + } await That(Act).DoesNotThrow(); } + + [Fact] + public async Task WhenInvokedTwice_ShouldFail() + { + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Once(); + } + + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly once, + but found it twice + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); + } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ThenTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ThenTests.cs index e11ebc9..70fa3f6 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ThenTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.ThenTests.cs @@ -18,23 +18,25 @@ public async Task Then_ShouldVerifyInOrder() sut.MyMethod(4); await That(sut.VerifyMock.Invoked.MyMethod(It.Is(3))).Then(m => m.Invoked.MyMethod(It.Is(4))); - await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(2))).Then(m => m.Invoked.MyMethod(It.Is(1)))) + await That(async Task () + => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(2))).Then(m => m.Invoked.MyMethod(It.Is(1)))) .Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(2), then - invoked method MyMethod(1) in order, - but it invoked method MyMethod(1) too early + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(2), then + invoked method MyMethod(1) in order, + but it invoked method MyMethod(1) too early - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), - [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) - ] - """); - await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))).Then(m => m.Invoked.MyMethod(It.Is(2)), m => m.Invoked.MyMethod(It.Is(3))); + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), + [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) + ] + """); + await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))) + .Then(m => m.Invoked.MyMethod(It.Is(2)), m => m.Invoked.MyMethod(It.Is(3))); } [Fact] @@ -47,58 +49,61 @@ public async Task Then_WhenNoMatch_ShouldReturnFalse() sut.MyMethod(3); sut.MyMethod(4); - await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(6))).Then(m => m.Invoked.MyMethod(It.Is(4)))) + await That(async Task () + => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(6))).Then(m => m.Invoked.MyMethod(It.Is(4)))) .Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(6), then - invoked method MyMethod(4) in order, - but it invoked method MyMethod(6) not at all + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(6), then + invoked method MyMethod(4) in order, + but it invoked method MyMethod(6) not at all - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), - [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) - ] - """); + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), + [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) + ] + """); - await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))).Then(m => m.Invoked.MyMethod(It.Is(6)), m => m.Invoked.MyMethod(It.Is(3)))) + await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))) + .Then(m => m.Invoked.MyMethod(It.Is(6)), m => m.Invoked.MyMethod(It.Is(3)))) .Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1), then - invoked method MyMethod(6), then - invoked method MyMethod(3) in order, - but it invoked method MyMethod(6) not at all + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1), then + invoked method MyMethod(6), then + invoked method MyMethod(3) in order, + but it invoked method MyMethod(6) not at all - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), - [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) - ] - """); + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), + [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) + ] + """); - await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))).Then(m => m.Invoked.MyMethod(It.Is(2)), m => m.Invoked.MyMethod(It.Is(6)))) + await That(async Task () => await That(sut.VerifyMock.Invoked.MyMethod(It.Is(1))) + .Then(m => m.Invoked.MyMethod(It.Is(2)), m => m.Invoked.MyMethod(It.Is(6)))) .Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1), then - invoked method MyMethod(2), then - invoked method MyMethod(6) in order, - but it invoked method MyMethod(6) not at all + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1), then + invoked method MyMethod(2), then + invoked method MyMethod(6) in order, + but it invoked method MyMethod(6) not at all - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), - [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) - ] - """); + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(2), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(3), + [3] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(4) + ] + """); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TimesTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TimesTests.cs index 65e276a..0c6de09 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TimesTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TimesTests.cs @@ -1,3 +1,5 @@ +using System.Threading; +using aweXpect.Chronology; using Mockolate; using Xunit.Sdk; @@ -84,5 +86,118 @@ invoked method MyMethod(1, false) according to the predicate n => n % 2 == 1, [*] """).AsWildcard(); } + + [Fact] + public async Task WhenInvokedInBackground_ShouldFail() + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Times(n => n % 3 == 2); + } + + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) according to the predicate n => n % 3 == 2, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(8)] + [InlineData(3)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + await Task.Delay(50, token); + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + await Task.Delay(50); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Times(n => n % 3 == 2) + .WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(8)] + [InlineData(3)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Run(async () => + { + await Task.Delay(50); + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + await Task.Delay(50); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Times(n => n % 3 == 2) + .Within(30.Seconds()); + + await backgroundTask; + } + + [Theory] + [InlineData(8)] + [InlineData(3)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Run(async () => + { + await Task.Delay(50); + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + await Task.Delay(50); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Times(n => n % 3 == 2) + .WithTimeout(30.Seconds()); + + await backgroundTask; + } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TwiceTests.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TwiceTests.cs index 8c0ce64..609a4ef 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TwiceTests.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.TwiceTests.cs @@ -1,4 +1,6 @@ -using Mockolate; +using System.Threading; +using aweXpect.Chronology; +using Mockolate; using Xunit.Sdk; namespace aweXpect.Mockolate.Tests; @@ -8,86 +10,196 @@ public sealed partial class ThatVerificationResultIs public sealed class TwiceTests { [Fact] - public async Task WhenInvokedTwice_ShouldSucceed() + public async Task WhenInvokedInBackground_ShouldFail() { - var mock = Mock.Create(); - - mock.MyMethod(1, false); - mock.MyMethod(1, false); + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Run(async () => + { + try + { + await Task.Delay(5000, token); + while (!token.IsCancellationRequested) + { + await Task.Delay(50, token).ConfigureAwait(false); + mock.MyMethod(1, false); + } + } + catch (OperationCanceledException) + { + // Ignore cancellation + } + }, CancellationToken.None); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + } - await That(Act).DoesNotThrow(); + await That(Act).Throws() + .WithMessage(""" + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly twice, + but never found it + + Interactions: + [] + """); + cts.Cancel(); + await backgroundTask; + } + + [Theory] + [InlineData(2)] + public async Task WhenInvokedInBackground_WithCancellation_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + using CancellationTokenSource cts = new(30.Seconds()); + CancellationToken token = cts.Token; + + Task backgroundTask = Task.Delay(50, token).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }, token); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice().WithCancellation(token); + + await backgroundTask; + } + + [Theory] + [InlineData(2)] + public async Task WhenInvokedInBackground_Within_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice().Within(30.Seconds()); + + await backgroundTask; } + + [Theory] + [InlineData(2)] + public async Task WhenInvokedInBackground_WithTimeout_ShouldSucceed(int invocationTimes) + { + IMyService mock = Mock.Create(); + + Task backgroundTask = Task.Delay(50).ContinueWith(_ => + { + for (int i = 0; i < invocationTimes; i++) + { + mock.MyMethod(1, false); + } + }); + + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice().WithTimeout(30.Seconds()); + + await backgroundTask; + } + [Fact] public async Task WhenInvokedMoreThanTwice_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); mock.MyMethod(1, false); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly twice, - but found it 3 times - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), - [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly twice, + but found it 3 times + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [1] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False), + [2] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); } [Fact] public async Task WhenInvokedNever_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly twice, - but never found it - - Interactions: - [] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly twice, + but never found it + + Interactions: + [] + """); } [Fact] public async Task WhenInvokedOnce_ShouldFail() { - var mock = Mock.Create(); + IMyService mock = Mock.Create(); mock.MyMethod(1, false); async Task Act() - => await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + } await That(Act).Throws() .WithMessage(""" - Expected that the ThatVerificationResultIs.IMyService mock - invoked method MyMethod(1, false) exactly twice, - but found it only once - - Interactions: - [ - [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) - ] - """); + Expected that the ThatVerificationResultIs.IMyService mock + invoked method MyMethod(1, false) exactly twice, + but found it only once + + Interactions: + [ + [0] invoke method aweXpect.Mockolate.Tests.ThatVerificationResultIs.IMyService.MyMethod(1, False) + ] + """); + } + + [Fact] + public async Task WhenInvokedTwice_ShouldSucceed() + { + IMyService mock = Mock.Create(); + + mock.MyMethod(1, false); + mock.MyMethod(1, false); + + async Task Act() + { + await That(mock.VerifyMock.Invoked.MyMethod(It.Is(1), It.Is(false))).Twice(); + } + + await That(Act).DoesNotThrow(); } } } diff --git a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.cs b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.cs index 68a0514..dacb76d 100644 --- a/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.cs +++ b/Tests/aweXpect.Mockolate.Tests/ThatVerificationResultIs.cs @@ -1,7 +1,4 @@ -using Mockolate; -using Xunit.Sdk; - -namespace aweXpect.Mockolate.Tests; +namespace aweXpect.Mockolate.Tests; public sealed partial class ThatVerificationResultIs { diff --git a/Tests/aweXpect.Mockolate.Tests/aweXpect.Mockolate.Tests.csproj b/Tests/aweXpect.Mockolate.Tests/aweXpect.Mockolate.Tests.csproj index ee95b69..a212c81 100644 --- a/Tests/aweXpect.Mockolate.Tests/aweXpect.Mockolate.Tests.csproj +++ b/Tests/aweXpect.Mockolate.Tests/aweXpect.Mockolate.Tests.csproj @@ -1,7 +1,7 @@  - +