Skip to content

Commit 4fa5c69

Browse files
SimonNyvallardalis
andauthored
Refactor AgainstExpression to Expression for Clarity and Consistency (#317)
* Update GuardAgainstExpressions method to be obsolete * Add GuardAgainstExpression * Update Expression summary * Update obsolete message * Rename the old expression to deprecated and the new expression to GuardAgainstExpressionExtensions * Add unit test for expression * Rename the old expression to GuardAgainstExpressionDeprecated * Update Guard.Against.Expression to Match Documentation Revised Guard.Against.Expression and Guard.Against.ExpressionAsync to throw ArgumentException when expressions evaluate to true, aligning with the documentation. This change ensures consistency and correctness in method behavior. * Update Tests for Guard.Against.Expression Changes Refactored unit tests for Guard.Against.Expression to match updated method logic. Tests now correctly handle scenarios where expressions evaluate to true, with adjustments to test conditions and error messages for clarity. --------- Co-authored-by: Steve Smith <[email protected]>
1 parent f8f81ed commit 4fa5c69

File tree

4 files changed

+219
-77
lines changed

4 files changed

+219
-77
lines changed

src/GuardClauses/GuardAgainstExpressionExtensions.cs

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,60 @@
11
using System;
22
using System.Threading.Tasks;
3+
using System.Runtime.CompilerServices;
34

45
namespace Ardalis.GuardClauses;
56

67
public static partial class GuardClauseExtensions
78
{
89
/// <summary>
9-
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
10+
/// Validates the <paramref name="input"/> using the specified <paramref name="func"/> and throws an <see cref="ArgumentException"/> if it evaluates to true.
11+
/// The <paramref name="func"/> should return true to indicate an invalid or undesirable state of the input.
12+
/// If <paramref name="func"/> returns true, an <see cref="ArgumentException"/> is thrown, signifying that the input is invalid.
1013
/// </summary>
11-
/// <typeparam name="T"></typeparam>
12-
/// <param name="func"></param>
13-
/// <param name="guardClause"></param>
14-
/// <param name="input"></param>
15-
/// <param name="message"></param>
16-
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
17-
/// <exception cref="ArgumentException"></exception>
18-
public static T AgainstExpression<T>(this IGuardClause guardClause,
14+
/// <typeparam name="T">The type of the input parameter.</typeparam>
15+
/// <param name="guardClause">The guard clause instance.</param>
16+
/// <param name="func">The function that evaluates the input. It should return true if the input is considered invalid or in a negative state.</param>
17+
/// <param name="input">The input to evaluate.</param>
18+
/// <param name="message">The message to include in the exception if the input is invalid.</param>
19+
/// <param name="parameterName">The name of the parameter to include in the thrown exception, captured automatically from the input expression.</param>
20+
/// <returns>The <paramref name="input"/> if the <paramref name="func"/> evaluates to false, indicating a valid state.</returns>
21+
/// <exception cref="ArgumentException">Thrown when the validation function returns true, indicating that the input is invalid.</exception>
22+
public static T Expression<T>(this IGuardClause guardClause,
1923
Func<T, bool> func,
2024
T input,
21-
string message) where T : struct
25+
string message,
26+
[CallerArgumentExpression("input")] string? parameterName = null) where T : struct
2227
{
23-
if (!func(input))
28+
if (func(input))
2429
{
25-
throw new ArgumentException(message);
30+
throw new ArgumentException(message, parameterName!);
2631
}
2732

2833
return input;
2934
}
3035

3136
/// <summary>
32-
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
37+
/// Validates the <paramref name="func"/> asynchronously and throws an <see cref="ArgumentException" /> if it evaluates to false for given <paramref name="input"/>
38+
/// The <paramref name="func"/> should return true to indicate an invalid or undesirable state.
39+
/// If <paramref name="func"/> returns true, indicating that the input is invalid, an <see cref="ArgumentException"/> is thrown.
3340
/// </summary>
34-
/// <typeparam name="T"></typeparam>
35-
/// <param name="func"></param>
36-
/// <param name="guardClause"></param>
37-
/// <param name="input"></param>
38-
/// <param name="message"></param>
41+
/// <typeparam name="T">The type of the input parameter.</typeparam>
42+
/// <param name="func">The function that evaluates the input. It should return true if the input is considered invalid or in a negative state.</param>
43+
/// <param name="guardClause">The guard clause instance.</param>
44+
/// <param name="input">The input to evaluate.</param>
45+
/// <param name="message">The message to include in the exception if the input is invalid.</param>
46+
/// <param name="parameterName">The name of the parameter to include in the thrown exception, captured automatically from the input expression.</param>
3947
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
40-
/// <exception cref="ArgumentException"></exception>
41-
public static async Task<T> AgainstExpressionAsync<T>(this IGuardClause guardClause,
48+
/// <exception cref="ArgumentException">Thrown when the validation function returns true, indicating that the input is invalid.</exception>
49+
public static async Task<T> ExpressionAsync<T>(this IGuardClause guardClause,
4250
Func<T, Task<bool>> func,
4351
T input,
44-
string message) where T : struct
45-
{
46-
if (!await func(input))
47-
{
48-
throw new ArgumentException(message);
49-
}
50-
51-
return input;
52-
}
53-
54-
/// <summary>
55-
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
56-
/// </summary>
57-
/// <typeparam name="T"></typeparam>
58-
/// <param name="func"></param>
59-
/// <param name="guardClause"></param>
60-
/// <param name="input"></param>
61-
/// <param name="message"></param>
62-
/// <param name="paramName">The name of the parameter that is invalid</param>
63-
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
64-
/// <exception cref="ArgumentException"></exception>
65-
public static T AgainstExpression<T>(this IGuardClause guardClause, Func<T, bool> func,
66-
T input, string message, string paramName) where T : struct
52+
string message,
53+
[CallerArgumentExpression("input")] string? parameterName = null) where T : struct
6754
{
68-
if (!func(input))
55+
if (await func(input))
6956
{
70-
throw new ArgumentException(message, paramName);
57+
throw new ArgumentException(message, parameterName!);
7158
}
7259

7360
return input;
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace Ardalis.GuardClauses;
5+
6+
public static partial class GuardClauseExtensions
7+
{
8+
/// <summary>
9+
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
10+
/// </summary>
11+
/// <typeparam name="T"></typeparam>
12+
/// <param name="func"></param>
13+
/// <param name="guardClause"></param>
14+
/// <param name="input"></param>
15+
/// <param name="message"></param>
16+
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
17+
/// <exception cref="ArgumentException"></exception>
18+
[Obsolete("Deprecated: Switch to Expression for validation.")]
19+
public static T AgainstExpression<T>(this IGuardClause guardClause,
20+
Func<T, bool> func,
21+
T input,
22+
string message) where T : struct
23+
{
24+
if (!func(input))
25+
{
26+
throw new ArgumentException(message);
27+
}
28+
29+
return input;
30+
}
31+
32+
/// <summary>
33+
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
34+
/// </summary>
35+
/// <typeparam name="T"></typeparam>
36+
/// <param name="func"></param>
37+
/// <param name="guardClause"></param>
38+
/// <param name="input"></param>
39+
/// <param name="message"></param>
40+
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
41+
/// <exception cref="ArgumentException"></exception>
42+
[Obsolete("Deprecated: Switch to ExpressionAsync for asynchronous validation.")]
43+
public static async Task<T> AgainstExpressionAsync<T>(this IGuardClause guardClause,
44+
Func<T, Task<bool>> func,
45+
T input,
46+
string message) where T : struct
47+
{
48+
if (!await func(input))
49+
{
50+
throw new ArgumentException(message);
51+
}
52+
53+
return input;
54+
}
55+
56+
/// <summary>
57+
/// Throws an <see cref="ArgumentException" /> if <paramref name="func"/> evaluates to false for given <paramref name="input"/>
58+
/// </summary>
59+
/// <typeparam name="T"></typeparam>
60+
/// <param name="func"></param>
61+
/// <param name="guardClause"></param>
62+
/// <param name="input"></param>
63+
/// <param name="message"></param>
64+
/// <param name="paramName">The name of the parameter that is invalid</param>
65+
/// <returns><paramref name="input"/> if the <paramref name="func"/> evaluates to true </returns>
66+
/// <exception cref="ArgumentException"></exception>
67+
[Obsolete("Deprecated: Switch to Expression for validation.")]
68+
public static T AgainstExpression<T>(this IGuardClause guardClause, Func<T, bool> func,
69+
T input, string message, string paramName) where T : struct
70+
{
71+
if (!func(input))
72+
{
73+
throw new ArgumentException(message, paramName);
74+
}
75+
76+
return input;
77+
}
78+
}

test/GuardClauses.UnitTests/GuardAgainstExpression.cs

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,66 +22,56 @@ public static IEnumerable<object[]> GetCustomStruct()
2222
};
2323
}
2424

25-
[Theory]
26-
[InlineData(10)]
27-
public void GivenIntegerWhenTheExpressionEvaluatesToTrueDoesNothing(int test)
25+
[Fact]
26+
public void GivenIntegerWhenTheExpressionEvaluatesToTrueThrowsException()
2827
{
29-
Guard.Against.AgainstExpression((x) => x == 10, test, "Value is not equal to 10");
28+
int testCase = 10;
29+
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x == 10, testCase, "Value cannot be 10"));
3030
}
3131

32-
[Theory]
33-
[InlineData(10)]
34-
public void GivenIntegerWhenTheExpressionEvaluatesToFalseThrowsException(int test)
32+
[Fact]
33+
public void GivenIntegerWhenTheExpressionEvaluatesToFalseDoesNothing()
3534
{
36-
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5, test, "Value is not equal to 10"));
35+
int testCase = 10;
36+
Guard.Against.Expression((x) => x == 5, testCase, "Value cannot be 5");
3737
}
3838

39-
[Theory]
40-
[InlineData(1.1)]
41-
public void GivenDoubleWhenTheExpressionEvaluatesToTrueDoesNothing(double test)
39+
[Fact]
40+
public void GivenDoubleWhenTheExpressionEvaluatesToTrueThrowsException()
4241
{
43-
Guard.Against.AgainstExpression((x) => x == 1.1, test, "Value is not equal to 1.1");
42+
double testCase = 1.1;
43+
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x == 1.1, testCase, "Value cannot be 1.1"));
4444
}
4545

46-
[Theory]
47-
[InlineData(1.1)]
48-
public void GivenDoubleWhenTheExpressionEvaluatesToFalseThrowsException(int test)
46+
[Fact]
47+
public void GivenDoubleWhenTheExpressionEvaluatesToFalseDoesNothing()
4948
{
50-
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5.0, test, "Value is not equal to 1.1"));
49+
double testCase = 1.1;
50+
Guard.Against.Expression((x) => x == 5.0, testCase, "Value cannot be 5.0");
5151
}
5252

5353
[Theory]
5454
[MemberData(nameof(GetCustomStruct))]
55-
public void GivenCustomStructWhenTheExpressionEvaluatesToTrueDoesNothing(CustomStruct test)
55+
public void GivenCustomStructWhenTheExpressionEvaluatesToTrueThrowsException(CustomStruct test)
5656
{
57-
Guard.Against.AgainstExpression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching");
57+
Assert.Throws<ArgumentException>(() => Guard.Against.Expression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching"));
5858
}
5959

6060
[Theory]
6161
[MemberData(nameof(GetCustomStruct))]
62-
public void GivenCustomStructWhenTheExpressionEvaluatesToFalseThrowsException(CustomStruct test)
62+
public void GivenCustomStructWhenTheExpressionEvaluatesToFalseDoesNothing(CustomStruct test)
6363
{
64-
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching"));
65-
}
66-
67-
[Theory]
68-
[InlineData(null, "Value does not fall within the expected range.")]
69-
[InlineData("Please provide correct value", "Please provide correct value")]
70-
public void ErrorMessageMatchesExpected(string customMessage, string expectedMessage)
71-
{
72-
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, customMessage));
73-
Assert.NotNull(exception);
74-
Assert.NotNull(exception.Message);
75-
Assert.Equal(expectedMessage, exception.Message);
64+
Guard.Against.Expression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching");
7665
}
7766

7867
[Fact]
7968
public void ErrorIncludesParamNameIfProvided()
8069
{
8170
string paramName = "testParamName";
82-
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, "custom message", paramName));
71+
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.Expression(x => x == 2, 2, "custom message", paramName));
8372
Assert.NotNull(exception);
8473
Assert.NotNull(exception.Message);
8574
Assert.Equal(paramName, exception.ParamName);
8675
}
76+
8777
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Ardalis.GuardClauses;
4+
using Xunit;
5+
6+
namespace GuardClauses.UnitTests;
7+
8+
public class GuardAgainstExpressionDeprecated
9+
{
10+
public struct CustomStruct
11+
{
12+
public string FieldName { get; set; }
13+
}
14+
15+
public static IEnumerable<object[]> GetCustomStruct()
16+
{
17+
yield return new object[] {
18+
new CustomStruct
19+
{
20+
FieldName = "FieldValue"
21+
}
22+
};
23+
}
24+
25+
[Theory]
26+
[InlineData(10)]
27+
public void GivenIntegerWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(int test)
28+
{
29+
Guard.Against.AgainstExpression((x) => x == 10, test, "Value is not equal to 10");
30+
}
31+
32+
[Theory]
33+
[InlineData(10)]
34+
public void GivenIntegerWhenTheAgainstExpressionEvaluatesToFalseThrowsException(int test)
35+
{
36+
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5, test, "Value is not equal to 10"));
37+
}
38+
39+
[Theory]
40+
[InlineData(1.1)]
41+
public void GivenDoubleWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(double test)
42+
{
43+
Guard.Against.AgainstExpression((x) => x == 1.1, test, "Value is not equal to 1.1");
44+
}
45+
46+
[Theory]
47+
[InlineData(1.1)]
48+
public void GivenDoubleWhenTheAgainstExpressionEvaluatesToFalseThrowsException(int test)
49+
{
50+
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x == 5.0, test, "Value is not equal to 1.1"));
51+
}
52+
53+
[Theory]
54+
[MemberData(nameof(GetCustomStruct))]
55+
public void GivenCustomStructWhenTheAgainstExpressionEvaluatesToTrueDoesNothing(CustomStruct test)
56+
{
57+
Guard.Against.AgainstExpression((x) => x.FieldName == "FieldValue", test, "FieldValue is not matching");
58+
}
59+
60+
[Theory]
61+
[MemberData(nameof(GetCustomStruct))]
62+
public void GivenCustomStructWhenTheAgainstExpressionEvaluatesToFalseThrowsException(CustomStruct test)
63+
{
64+
Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression((x) => x.FieldName == "FailThis", test, "FieldValue is not matching"));
65+
}
66+
67+
[Theory]
68+
[InlineData(null, "Value does not fall within the expected range.")]
69+
[InlineData("Please provide correct value", "Please provide correct value")]
70+
public void ErrorMessageMatchesAgainstExpected(string customMessage, string expectedMessage)
71+
{
72+
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, customMessage));
73+
Assert.NotNull(exception);
74+
Assert.NotNull(exception.Message);
75+
Assert.Equal(expectedMessage, exception.Message);
76+
}
77+
78+
[Fact]
79+
public void ErrorIncludesParamNameIfProvidedInAgainstExpression()
80+
{
81+
string paramName = "testParamName";
82+
var exception = Assert.Throws<ArgumentException>(() => Guard.Against.AgainstExpression(x => x == 1, 2, "custom message", paramName));
83+
Assert.NotNull(exception);
84+
Assert.NotNull(exception.Message);
85+
Assert.Equal(paramName, exception.ParamName);
86+
}
87+
}

0 commit comments

Comments
 (0)