Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add extensions for OccurrenceConstraint that reads more fluently #2047

Merged
merged 10 commits into from Jan 1, 2023
36 changes: 36 additions & 0 deletions Src/FluentAssertions/Extensions/OccurrenceConstraintExtensions.cs
@@ -0,0 +1,36 @@
using System;

namespace FluentAssertions.Extensions;

/// <summary>
/// Provides extensions to write <see cref="OccurrenceConstraint" />s with fluent syntax
/// </summary>
public static class OccurrenceConstraintExtensions
{
/// <summary>
/// This is the equivalent to <see cref="Exactly.Times(int)" />
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="times"/> is less than zero.</exception>
public static OccurrenceConstraint TimesExactly(this int times)
IT-VBFK marked this conversation as resolved.
Show resolved Hide resolved
{
return Exactly.Times(times);
}

/// <summary>
/// This is the equivalent to <see cref="AtMost.Times(int)" />
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="times"/> is less than zero.</exception>
public static OccurrenceConstraint TimesOrLess(this int times)
{
return AtMost.Times(times);
}

/// <summary>
/// This is the equivalent to <see cref="AtLeast.Times(int)" />
/// </summary>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="times"/> is less than zero.</exception>
public static OccurrenceConstraint TimesOrMore(this int times)
{
return AtLeast.Times(times);
}
}
Expand Up @@ -1476,6 +1476,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
Expand Up @@ -1489,6 +1489,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
Expand Up @@ -1476,6 +1476,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
Expand Up @@ -1476,6 +1476,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
Expand Up @@ -1427,6 +1427,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
Expand Up @@ -1476,6 +1476,12 @@ namespace FluentAssertions.Extensions
public static double TotalMicroseconds(this System.TimeSpan self) { }
public static double TotalNanoseconds(this System.TimeSpan self) { }
}
public static class OccurrenceConstraintExtensions
{
public static FluentAssertions.OccurrenceConstraint TimesExactly(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrLess(this int times) { }
public static FluentAssertions.OccurrenceConstraint TimesOrMore(this int times) { }
}
}
namespace FluentAssertions.Formatting
{
Expand Down
11 changes: 10 additions & 1 deletion Tests/FluentAssertions.Specs/OccurrenceConstraintSpecs.cs
@@ -1,5 +1,6 @@
using System;
using FluentAssertions.Execution;
using FluentAssertions.Extensions;
using Xunit;
using Xunit.Sdk;

Expand All @@ -17,6 +18,8 @@ public class OccurrenceConstraintSpecs
{ AtLeast.Thrice(), 4 },
{ AtLeast.Times(4), 4 },
{ AtLeast.Times(4), 5 },
{ 4.TimesOrMore(), 4 },
{ 4.TimesOrMore(), 5 },
{ AtMost.Once(), 0 },
{ AtMost.Once(), 1 },
{ AtMost.Twice(), 1 },
Expand All @@ -25,17 +28,20 @@ public class OccurrenceConstraintSpecs
{ AtMost.Thrice(), 3 },
{ AtMost.Times(4), 3 },
{ AtMost.Times(4), 4 },
{ 4.TimesOrLess(), 4 },
{ 4.TimesOrLess(), 1 },
{ Exactly.Once(), 1 },
{ Exactly.Twice(), 2 },
{ Exactly.Thrice(), 3 },
{ Exactly.Times(4), 4 },
{ 4.TimesExactly(), 4 },
{ LessThan.Twice(), 1 },
{ LessThan.Thrice(), 2 },
{ LessThan.Times(4), 3 },
{ MoreThan.Once(), 2 },
{ MoreThan.Twice(), 3 },
{ MoreThan.Thrice(), 4 },
{ MoreThan.Times(4), 5 },
{ MoreThan.Times(4), 5 }
};

[Theory]
Expand All @@ -54,10 +60,12 @@ public void Occurrence_constraint_passes(OccurrenceConstraint constraint, int oc
{ AtLeast.Twice(), 1 },
{ AtLeast.Thrice(), 2 },
{ AtLeast.Times(4), 3 },
{ 4.TimesOrMore(), 3 },
{ AtMost.Once(), 2 },
{ AtMost.Twice(), 3 },
{ AtMost.Thrice(), 4 },
{ AtMost.Times(4), 5 },
{ 4.TimesOrLess(), 5 },
{ Exactly.Once(), 0 },
{ Exactly.Once(), 2 },
{ Exactly.Twice(), 1 },
Expand All @@ -66,6 +74,7 @@ public void Occurrence_constraint_passes(OccurrenceConstraint constraint, int oc
{ Exactly.Thrice(), 4 },
{ Exactly.Times(4), 3 },
{ Exactly.Times(4), 5 },
{ 4.TimesExactly(), 1 },
{ LessThan.Twice(), 2 },
{ LessThan.Twice(), 3 },
{ LessThan.Thrice(), 3 },
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -17,6 +17,7 @@ sidebar:
* Added `ThatAre[Not]Abstract`, `ThatAre[Not]Static` and `ThatAre[Not]Virtual` properties for filtering in `PropertyInfoSelector.cs` - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054)
* Added `BeOneOf` methods for object comparisons and `IComparable`s - [#2028](https://github.com/fluentassertions/fluentassertions/pull/2028)
* Added `BeCloseTo` and `NotBeCloseTo` to `TimeOnly` - [#2030](https://github.com/fluentassertions/fluentassertions/pull/2030)
* Added new extension methods to be able to write `Exactly.Times(n)`, `AtLeast.Times(n)` and `AtMost.Times(n)` in a more fluent way - [#2047](https://github.com/fluentassertions/fluentassertions/pull/2047)

### Fixes
* Quering properties on classes, e.g. `typeof(MyClass).Properties()`, now also includes static properties - [#2054](https://github.com/fluentassertions/fluentassertions/pull/2054)
Expand Down
8 changes: 8 additions & 0 deletions docs/_pages/strings.md
Expand Up @@ -115,3 +115,11 @@ And if that's not enough, you can assert on the number of matches of a regular e
someString.Should().MatchRegex("h.*\\sworld.$", Exactly.Once());
someString.Should().MatchRegex(new Regex("h.*\\sworld.$"), AtLeast.Twice());
```

If you prefer a more fluent syntax than `Exactly.Times(4)`, `AtLeast.Times(4)` and `AtMost.Times(4)` reads, you can do the following:

```csharp
theString.Should().Contain("is a", 4.TimesExactly()); // equivalent to Exactly.Times(4)
theString.Should().Contain("is a", 4.TimesOrMore()); // equivalent to AtLeast.Times(4)
theString.Should().Contain("is a", 4.TimesOrLess()); // equivalent to AtMost.Times(4)
```