diff --git a/Docs/pages/docs/expectations/10-timespan.md b/Docs/pages/docs/expectations/10-timespan.md
index f3bc93aa7..80a2e77cc 100644
--- a/Docs/pages/docs/expectations/10-timespan.md
+++ b/Docs/pages/docs/expectations/10-timespan.md
@@ -82,6 +82,28 @@ await Expect.That(subject).IsLessThan(42).Within(TimeSpan.FromSeconds(2))
.Because("we accept values less than 0:44 (0:42 ± 2s)");
```
+## Between
+
+You can verify that the `TimeSpan` is between two values:
+
+```csharp
+TimeSpan subject = TimeSpan.FromSeconds(42);
+
+await Expect.That(subject).IsBetween(TimeSpan.FromSeconds(40)).And(TimeSpan.FromSeconds(50));
+```
+
+You can also specify a tolerance:
+
+```csharp
+TimeSpan subject = TimeSpan.FromSeconds(42);
+
+await Expect.That(subject)
+ .IsBetween(43.Seconds())
+ .And(45.Seconds())
+ .Within(1.Seconds())
+ .Because("it should expand the interval by 1 second");
+```
+
## Positive / negative
You can verify that the `TimeSpan` is positive or negative:
diff --git a/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs b/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs
new file mode 100644
index 000000000..49d7805bb
--- /dev/null
+++ b/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs
@@ -0,0 +1,109 @@
+using System;
+using aweXpect.Core;
+using aweXpect.Core.Constraints;
+using aweXpect.Customization;
+using aweXpect.Helpers;
+using aweXpect.Options;
+using aweXpect.Results;
+
+namespace aweXpect;
+
+public static partial class ThatNullableTimeSpan
+{
+ ///
+ /// Verifies that the subject is between the …
+ ///
+ public static BetweenResult>, TimeSpan?> IsBetween(
+ this IThat source,
+ TimeSpan? minimum)
+ {
+ TimeTolerance tolerance = new();
+ return new BetweenResult>, TimeSpan?>(maximum
+ => new TimeToleranceResult>(
+ source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
+ new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)),
+ source,
+ tolerance));
+ }
+
+ ///
+ /// Verifies that the subject is not between the …
+ ///
+ public static BetweenResult>, TimeSpan?> IsNotBetween(
+ this IThat source,
+ TimeSpan? minimum)
+ {
+ TimeTolerance tolerance = new();
+ return new BetweenResult>, TimeSpan?>(maximum
+ => new TimeToleranceResult>(
+ source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
+ new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()),
+ source,
+ tolerance));
+ }
+
+ private sealed class IsBetweenConstraint(
+ string it,
+ ExpectationGrammars grammars,
+ TimeSpan? minimum,
+ TimeSpan? maximum,
+ TimeTolerance tolerance)
+ : ConstraintResult.WithNotNullValue(it, grammars),
+ IValueConstraint
+ {
+ public ConstraintResult IsMetBy(TimeSpan? actual)
+ {
+ Actual = actual;
+ if (actual is null && minimum is null && maximum is null)
+ {
+ Outcome = Outcome.Success;
+ }
+ else if (actual is null || minimum is null || maximum is null)
+ {
+ Outcome = IsNegated ? Outcome.Success : Outcome.Failure;
+ }
+ else
+ {
+ TimeSpan timeTolerance = tolerance.Tolerance
+ ?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get();
+ if (IsNegated)
+ {
+ timeTolerance = timeTolerance.Negate();
+ }
+
+ Outcome = actual.Value.Add(timeTolerance) >= minimum && actual.Value.Add(timeTolerance.Negate()) <= maximum
+ ? Outcome.Success
+ : Outcome.Failure;
+ }
+
+ return this;
+ }
+
+ protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append("is between ");
+ Formatter.Format(stringBuilder, minimum);
+ stringBuilder.Append(" and ");
+ Formatter.Format(stringBuilder, maximum);
+ stringBuilder.Append(tolerance);
+ }
+
+ protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append(It).Append(" was ");
+ Formatter.Format(stringBuilder, Actual);
+ }
+
+ protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append("is not between ");
+ Formatter.Format(stringBuilder, minimum);
+ stringBuilder.Append(" and ");
+ Formatter.Format(stringBuilder, maximum);
+ stringBuilder.Append(tolerance);
+ }
+
+ protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
+ => AppendNormalResult(stringBuilder, indentation);
+ }
+}
diff --git a/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs b/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs
new file mode 100644
index 000000000..0eecb7c9b
--- /dev/null
+++ b/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs
@@ -0,0 +1,105 @@
+using System;
+using aweXpect.Core;
+using aweXpect.Core.Constraints;
+using aweXpect.Customization;
+using aweXpect.Helpers;
+using aweXpect.Options;
+using aweXpect.Results;
+
+namespace aweXpect;
+
+public static partial class ThatTimeSpan
+{
+ ///
+ /// Verifies that the subject is between the …
+ ///
+ public static BetweenResult>, TimeSpan?> IsBetween(
+ this IThat source,
+ TimeSpan? minimum)
+ {
+ TimeTolerance tolerance = new();
+ return new BetweenResult>, TimeSpan?>(maximum
+ => new TimeToleranceResult>(
+ source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
+ new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)),
+ source,
+ tolerance));
+ }
+
+ ///
+ /// Verifies that the subject is not between the …
+ ///
+ public static BetweenResult>, TimeSpan?> IsNotBetween(
+ this IThat source,
+ TimeSpan? minimum)
+ {
+ TimeTolerance tolerance = new();
+ return new BetweenResult>, TimeSpan?>(maximum
+ => new TimeToleranceResult>(
+ source.Get().ExpectationBuilder.AddConstraint((it, grammars) =>
+ new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()),
+ source,
+ tolerance));
+ }
+
+ private sealed class IsBetweenConstraint(
+ string it,
+ ExpectationGrammars grammars,
+ TimeSpan? minimum,
+ TimeSpan? maximum,
+ TimeTolerance tolerance)
+ : ConstraintResult.WithNotNullValue(it, grammars),
+ IValueConstraint
+ {
+ public ConstraintResult IsMetBy(TimeSpan actual)
+ {
+ Actual = actual;
+ if (minimum is null || maximum is null)
+ {
+ Outcome = IsNegated ? Outcome.Success : Outcome.Failure;
+ }
+ else
+ {
+ TimeSpan timeTolerance = tolerance.Tolerance
+ ?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get();
+ if (IsNegated)
+ {
+ timeTolerance = timeTolerance.Negate();
+ }
+
+ Outcome = actual.Add(timeTolerance) >= minimum && actual.Add(timeTolerance.Negate()) <= maximum
+ ? Outcome.Success
+ : Outcome.Failure;
+ }
+
+ return this;
+ }
+
+ protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append("is between ");
+ Formatter.Format(stringBuilder, minimum);
+ stringBuilder.Append(" and ");
+ Formatter.Format(stringBuilder, maximum);
+ stringBuilder.Append(tolerance);
+ }
+
+ protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append(It).Append(" was ");
+ Formatter.Format(stringBuilder, Actual);
+ }
+
+ protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
+ {
+ stringBuilder.Append("is not between ");
+ Formatter.Format(stringBuilder, minimum);
+ stringBuilder.Append(" and ");
+ Formatter.Format(stringBuilder, maximum);
+ stringBuilder.Append(tolerance);
+ }
+
+ protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
+ => AppendNormalResult(stringBuilder, indentation);
+ }
+}
diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
index e456260c7..92f975821 100644
--- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
+++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
@@ -653,12 +653,14 @@ namespace aweXpect
}
public static class ThatNullableTimeSpan
{
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { }
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
@@ -881,12 +883,14 @@ namespace aweXpect
}
public static class ThatTimeSpan
{
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { }
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
index f6bf8cc88..938dd8f57 100644
--- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
+++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
@@ -469,12 +469,14 @@ namespace aweXpect
}
public static class ThatNullableTimeSpan
{
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { }
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
@@ -928,12 +930,14 @@ namespace aweXpect
}
public static class ThatTimeSpan
{
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { }
public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { }
+ public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { }
public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { }
diff --git a/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs b/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs
index 039c07e1d..a02e70e94 100644
--- a/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs
+++ b/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs
@@ -11,7 +11,12 @@ public sealed class CustomizeSettingsTests
[Fact]
public async Task DefaultCheckInterval_ShouldBeUsedInTimeComparisons()
{
+#if DEBUG
TimeSpan timeout = 2.Seconds();
+#else
+ TimeSpan timeout = 4.Seconds();
+#endif
+
ChangingClass sut1 = new();
ChangingClass sut2 = new();
diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs
new file mode 100644
index 000000000..69c8fd5a5
--- /dev/null
+++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs
@@ -0,0 +1,254 @@
+namespace aweXpect.Tests;
+
+public sealed partial class ThatTimeSpan
+{
+ public sealed class IsBetween
+ {
+ public sealed class Tests
+ {
+ [Fact]
+ public async Task WhenMaximumIsNull_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = null;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and ,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumIsNull_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = null;
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMaximumAreMaxValue_ShouldSucceed()
+ {
+ TimeSpan subject = TimeSpan.MaxValue;
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMinimumAreMinValue_ShouldSucceed()
+ {
+ TimeSpan subject = TimeSpan.MinValue;
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsBetweenMinimumAndMaximum_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsEarlierThanMinimum_ShouldFail()
+ {
+ TimeSpan subject = EarlierTime();
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsLaterThanMaximum_ShouldFail()
+ {
+ TimeSpan subject = LaterTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMaximum_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMinimum_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = subject;
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+ }
+
+ public sealed class WithinTests
+ {
+ [Fact]
+ public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan subject = LaterTime(4);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan subject = EarlierTime(4);
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = LaterTime(-4);
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime(-4);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMaximumTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = LaterTime(3);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMinimumTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = EarlierTime(3);
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+ }
+ }
+}
diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs
new file mode 100644
index 000000000..8b8aece4a
--- /dev/null
+++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs
@@ -0,0 +1,259 @@
+namespace aweXpect.Tests;
+
+public sealed partial class ThatTimeSpan
+{
+ public sealed class IsNotBetween
+ {
+ public sealed class Tests
+ {
+ [Fact]
+ public async Task WhenMaximumIsNull_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = null;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and ,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumIsNull_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = null;
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMaximumAreMaxValue_ShouldFail()
+ {
+ TimeSpan subject = TimeSpan.MaxValue;
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMinimumAreMinValue_ShouldFail()
+ {
+ TimeSpan subject = TimeSpan.MinValue;
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsNotBetweenMinimumAndMaximum_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsEarlierThanMinimum_ShouldSucceed()
+ {
+ TimeSpan subject = EarlierTime();
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsLaterThanMaximum_ShouldSucceed()
+ {
+ TimeSpan subject = LaterTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMaximum_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = EarlierTime();
+ TimeSpan maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMinimum_ShouldFail()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = subject;
+ TimeSpan maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+ }
+
+ public sealed class WithinTests
+ {
+ [Fact]
+ public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = LaterTime(2);
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = EarlierTime(2);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = LaterTime(2);
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime(2);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMaximumTolerance_ShouldFail()
+ {
+ TimeSpan subject = EarlierTime(3);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMinimumTolerance_ShouldFail()
+ {
+ TimeSpan subject = LaterTime(3);
+ TimeSpan minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+ }
+ }
+}
diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs
new file mode 100644
index 000000000..0c8df20ad
--- /dev/null
+++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs
@@ -0,0 +1,257 @@
+namespace aweXpect.Tests;
+
+public sealed partial class ThatTimeSpan
+{
+ public sealed partial class Nullable
+ {
+ public sealed class IsBetween
+ {
+ public sealed class Tests
+ {
+ [Fact]
+ public async Task WhenMaximumIsNull_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = null;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and ,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumIsNull_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = null;
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMaximumAreMaxValue_ShouldSucceed()
+ {
+ TimeSpan? subject = TimeSpan.MaxValue;
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan? maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMinimumAreMinValue_ShouldSucceed()
+ {
+ TimeSpan? subject = TimeSpan.MinValue;
+ TimeSpan? minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsBetweenMinimumAndMaximum_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsEarlierThanMinimum_ShouldFail()
+ {
+ TimeSpan? subject = EarlierTime();
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsLaterThanMaximum_ShouldFail()
+ {
+ TimeSpan? subject = LaterTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMaximum_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMinimum_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+ }
+
+ public sealed class WithinTests
+ {
+ [Fact]
+ public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan? subject = LaterTime(4);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan? subject = EarlierTime(4);
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = LaterTime(-4);
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime(-4);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMaximumTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = LaterTime(3);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMinimumTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = EarlierTime(3);
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+ }
+ }
+ }
+}
diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs
new file mode 100644
index 000000000..f516fd884
--- /dev/null
+++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs
@@ -0,0 +1,262 @@
+namespace aweXpect.Tests;
+
+public sealed partial class ThatTimeSpan
+{
+ public sealed partial class Nullable
+ {
+ public sealed class IsNotBetween
+ {
+ public sealed class Tests
+ {
+ [Fact]
+ public async Task WhenMaximumIsNull_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = null;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and ,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenMinimumIsNull_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = null;
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMaximumAreMaxValue_ShouldFail()
+ {
+ TimeSpan? subject = TimeSpan.MaxValue;
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectAndMinimumAreMinValue_ShouldFail()
+ {
+ TimeSpan? subject = TimeSpan.MinValue;
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsEarlierThanMinimum_ShouldSucceed()
+ {
+ TimeSpan? subject = EarlierTime();
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsLaterThanMaximum_ShouldSucceed()
+ {
+ TimeSpan? subject = LaterTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsNotBetweenMinimumAndMaximum_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMaximum_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime();
+ TimeSpan? maximum = subject;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenSubjectIsSameAsMinimum_ShouldFail()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = subject;
+ TimeSpan? maximum = LaterTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum);
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)},
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+ }
+
+ public sealed class WithinTests
+ {
+ [Fact]
+ public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = LaterTime(2);
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime(2);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = LaterTime(2);
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldSucceed()
+ {
+ TimeSpan? subject = CurrentTime();
+ TimeSpan? minimum = EarlierTime(2);
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMaximumTolerance_ShouldFail()
+ {
+ TimeSpan? subject = EarlierTime(3);
+ TimeSpan minimum = TimeSpan.MinValue;
+ TimeSpan? maximum = CurrentTime();
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+
+ [Fact]
+ public async Task WhenValueIsWithinTheMinimumTolerance_ShouldFail()
+ {
+ TimeSpan? subject = LaterTime(3);
+ TimeSpan? minimum = CurrentTime();
+ TimeSpan maximum = TimeSpan.MaxValue;
+
+ async Task Act()
+ => await That(subject).IsNotBetween(minimum).And(maximum)
+ .Within(3.Seconds());
+
+ await That(Act).Throws()
+ .WithMessage($"""
+ Expected that subject
+ is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03,
+ but it was {Formatter.Format(subject)}
+ """);
+ }
+ }
+ }
+ }
+}