Skip to content

Commit dc3ad7e

Browse files
authored
Merge pull request #1 from andrej-dyck/feature/check-expressions
Add check expressions
2 parents 41eea49 + 5c20a9c commit dc3ad7e

File tree

5 files changed

+126
-9
lines changed

5 files changed

+126
-9
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using NUnit.Framework;
2+
using NUnit.Framework.Internal;
3+
4+
namespace DotNet.Extensions.Require.Test;
5+
6+
public sealed class ChecksTests
7+
{
8+
[Test]
9+
public void Check_ThrowsCheckFailed_WhenRequirementIsNotSatisfied() =>
10+
Assert.Throws<CheckFailed>(
11+
() => 1.Check(v => v != 1, expectation: "message")
12+
)?.WithMessage("message");
13+
14+
[Test]
15+
public void Check_DoesNotThrow_WhenRequirementIsSatisfied() =>
16+
Assert.DoesNotThrow(
17+
() => 1.Check(v => v == 1, expectation: string.Empty)
18+
);
19+
20+
[Test]
21+
public void Check_ThrowsBuiltException_WhenRequirementIsNotSatisfied() =>
22+
Assert.Throws<SomeExceptionType>(
23+
() => 1.Check(v => v != 1, () => new SomeExceptionType("message"))
24+
)?.WithMessage("message");
25+
26+
[Test]
27+
public void Check_CanConstructException_WithTheValue() =>
28+
Assert.Throws<SomeExceptionType>(
29+
() => 1.Check(v => v != 1, v => new SomeExceptionType($"message with {v}"))
30+
)?.WithMessage("message with 1");
31+
32+
[Test]
33+
public void Check_DoesNotBuildException_WhenRequirementIsSatisfied() => Assert.Multiple(
34+
() =>
35+
{
36+
Assert.DoesNotThrow(
37+
() => 1.Check(v => v == 1, expectation: () => throw new NUnitException("must not be called"))
38+
);
39+
Assert.DoesNotThrow(
40+
() => 1.Check(v => v == 1, exception: () => throw new NUnitException("must not be called"))
41+
);
42+
}
43+
);
44+
45+
[Test]
46+
public void Check_DoesNotConstructException_WhenRequirementIsSatisfied() => Assert.Multiple(
47+
() =>
48+
{
49+
Assert.DoesNotThrow(
50+
() => 1.Check(
51+
v => v == 1,
52+
expectation: v => throw new NUnitException($"must not be called for {v}"))
53+
);
54+
Assert.DoesNotThrow(
55+
() => 1.Check(v => v == 1, exception: v => throw new NUnitException($"must not be called for {v}"))
56+
);
57+
}
58+
);
59+
60+
[Test]
61+
public void Check_ReturnsTheValueOnWhichItIsCalled_WhenRequirementIsSatisfied() => Assert.Multiple(
62+
() =>
63+
{
64+
Assert.AreEqual(1, 1.Check(value => value == 1, string.Empty));
65+
Assert.AreEqual(1, 1.Check(value => value == 1, () => string.Empty));
66+
Assert.AreEqual(1, 1.Check(value => value == 1, value => $"{value}"));
67+
Assert.AreEqual(1, 1.Check(value => value == 1, () => new CheckFailed(string.Empty)));
68+
Assert.AreEqual(1, 1.Check(value => value == 1, value => new CheckFailed($"{value}")));
69+
}
70+
);
71+
}
72+
73+
public sealed class SomeExceptionType : Exception
74+
{
75+
public SomeExceptionType(string? message = null) : base(message) { }
76+
}

DotNet.Extensions.Require.Test/PreconditionsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void Require_DoesNotThrow_WhenConditionIsTrue() =>
2020
);
2121

2222
[Test]
23-
public void Require_Returns_TheValueOnWhichItIsCalled_WhenConditionIsTrue() =>
23+
public void Require_ReturnsTheValueOnWhichItIsCalled_WhenConditionIsTrue() =>
2424
Assert.AreEqual(
2525
expected: 1,
2626
actual: Require(subject: 1, condition: true)
@@ -91,7 +91,7 @@ protected override T Require<T>(T subject, Predicate<T> requirement, string expe
9191
subject.Require(requirement, expectation: () => expectation);
9292

9393
[Test]
94-
public void Require_DoesNotCreateExpectationMessage_WhenRequirementYieldsTrue() =>
94+
public void Require_DoesNotCreateExpectationMessage_WhenRequirementIsSatisfied() =>
9595
Assert.DoesNotThrow(
9696
() => 1.Require(requirement: _ => true, () => throw new NUnitException("must not be called"))
9797
);
@@ -109,7 +109,7 @@ public void Require_CanUseTheValue_ToConstructTheExpectation() =>
109109
)?.WithMessage("message with 1");
110110

111111
[Test]
112-
public void Require_DoesNotBuildExpectationMessage_WhenConditionIsTrue() =>
112+
public void Require_DoesNotBuildExpectationMessage_WhenRequirementIsSatisfied() =>
113113
Assert.DoesNotThrow(
114114
() => 1.Require(requirement: _ => true, value => throw new NUnitException($"must not be called for {value}"))
115115
);

DotNet.Extensions.Require/Checks.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace DotNet.Extensions.Require;
2+
3+
public static class Checks
4+
{
5+
public static T Check<T>(this T @this, Predicate<T> requirement, string expectation) =>
6+
requirement(@this) ? @this : throw new CheckFailed(expectation);
7+
8+
public static T Check<T>(this T @this, Predicate<T> requirement, Func<string> expectation) =>
9+
requirement(@this) ? @this : throw new CheckFailed(expectation());
10+
11+
public static T Check<T>(this T @this, Predicate<T> requirement, Func<T, string> expectation) =>
12+
requirement(@this) ? @this : throw new CheckFailed(expectation(@this));
13+
14+
public static T Check<T>(this T @this, Predicate<T> requirement, Func<Exception> exception) =>
15+
requirement(@this) ? @this : throw exception();
16+
17+
public static T Check<T>(this T @this, Predicate<T> requirement, Func<T, Exception> exception) =>
18+
requirement(@this) ? @this : throw exception(@this);
19+
}
20+
21+
public sealed class CheckFailed : Exception
22+
{
23+
public CheckFailed(string? message) : base(message) { }
24+
}

DotNet.Extensions.Require/DotNet.Extensions.Require.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<Description>☑ Require Expressions - A pre-condition checks library 📦 for .Net via extensions methods on types.</Description>
66
<PackageTags>preconditions;require;guard;code-contracts;argument-checks;extension-methods</PackageTags>
77

8-
<Version>1.0.0</Version>
8+
<Version>1.1.0</Version>
99
<Authors>Andrej Dyck</Authors>
1010

1111
<PackageReadmeFile>README.md</PackageReadmeFile>

README.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ Reservation Request(DateTime now, DateTime reservationDate, int seats) =>
2121
dotnet add package Require-Expressions
2222
```
2323

24-
## Examples
24+
## Examples for `Require`
2525

26-
**Basic `Require` function**
26+
Basic `Require` function
2727
```csharp
2828
var requestedSeats = seats.Require(
2929
condition: seats > 0,
3030
expectation: "expected: seats > 0"
31-
);
31+
); // throws ArgumentException with expectation as message when condition is not met
3232
```
3333

34-
**`Require` functions with lazily constructed expectation messages**
34+
`Require` functions with lazily constructed expectation messages
3535
```csharp
3636
var requestedSeats = seats.Require(
3737
condition: seats > 0,
@@ -44,7 +44,7 @@ var requestedDate = reservationDate.Require(
4444
);
4545
```
4646

47-
**`Require` functions with predicate for convenience**
47+
`Require` functions with predicate for convenience
4848
```csharp
4949
var requestedSeats = seats.Require(
5050
condition: s => s > 0,
@@ -57,6 +57,23 @@ var requestedDate = reservationDate.Require(
5757
);
5858
```
5959

60+
## Examples for `Check`
61+
62+
For checks other than on arguments, the `Check` function can be used
63+
```csharp
64+
var reservationRequest = JsonSerializer.Deserialize<Reservation>(request)
65+
.Check(requirement: r => r.Seats > 0, expectation: "expected: seats > 0")
66+
.Check(r => r.Date > now, r => $"Reservation date {r.Date} must be in the future")
67+
// throws CheckFailed with expectation as message when requirement is not satisfied
68+
```
69+
70+
With `Check`, custom exceptions can be constructed
71+
```csharp
72+
var reservationRequest = JsonSerializer.Deserialize<Reservation>(request)
73+
.Check(r => r.Seats > 0, exception: () => new SeatsMustBePositive())
74+
.Check(r => r.Date > now, exception: r => new MustBeFutureDateReservateion(r.Date))
75+
```
76+
6077
## Q&A
6178

6279
**Why yet another preconditions library?**

0 commit comments

Comments
 (0)