Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion src/GuardClauses/GuardAgainstNullExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static T Null<T>([JetBrainsNotNull] this IGuardClause guardClause,
#else
public static T Null<T>([JetBrainsNotNull] this IGuardClause guardClause,
[NotNull, JetBrainsNotNull][ValidatedNotNull][JetBrainsNoEnumeration] T input,
[JetBrainsNotNull][CallerArgumentExpression("input")] string? parameterName = null,
[JetBrainsNotNull][JetBrainsInvokerParameterName][CallerArgumentExpression("input")] string? parameterName = null,
string? message = null)
#endif
{
Expand Down Expand Up @@ -206,5 +206,38 @@ public static T Default<T>([JetBrainsNotNull] this IGuardClause guardClause,

return input;
}


/// <summary>
/// Throws an <see cref="ArgumentNullException"/> if <paramref name="input"/> is null
/// Throws an <see cref="ArgumentException" /> if <paramref name="input"/> doesn't satisfy the <paramref name="predicate"/> function.
/// </summary>
/// <param name="guardClause"></param>
/// <param name="input"></param>
/// <param name="parameterName"></param>
/// <param name="predicate"></param>
/// <param name="message">Optional. Custom error message</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
#if NETSTANDARD || NETFRAMEWORK
public static T NullOrInvalidInput<T>([JetBrainsNotNull] this IGuardClause guardClause,
[JetBrainsNotNull] T input,
[JetBrainsNotNull][JetBrainsInvokerParameterName] string parameterName,
Func<T, bool> predicate,
string? message = null)
#else
public static T NullOrInvalidInput<T>([JetBrainsNotNull] this IGuardClause guardClause,
[JetBrainsNotNull] T input,
[JetBrainsNotNull][JetBrainsInvokerParameterName] string parameterName,
Func<T, bool> predicate,
string? message = null)
#endif
{
Guard.Against.Null(input, parameterName, message);

return Guard.Against.InvalidInput(input, parameterName, predicate, message);
}
}
}
92 changes: 92 additions & 0 deletions test/GuardClauses.UnitTests/GuardAgainstNullOrInvalidInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Ardalis.GuardClauses;
using Xunit;

namespace GuardClauses.UnitTests
{
public class GuardAgainstNullOrInvalidInput
{
[Theory]
[ClassData(typeof(ArgumentNullExceptionClassData))]
public void ThrowsArgumentNullExceptionWhenInputIsNull(string input, Func<string, bool> func)
{
Assert.Throws<ArgumentNullException>("string",
() => Guard.Against.NullOrInvalidInput(input, "string", func));
}

[Theory]
[ClassData(typeof(ArgumentExceptionClassData))]
public void ThrowsArgumentExceptionWhenInputIsInvalid(string input, Func<string, bool> func)
{
Assert.Throws<ArgumentException>("string",
() => Guard.Against.NullOrInvalidInput(input, "string", func));
}

[Theory]
[ClassData(typeof(ValidClassData))]
public void ReturnsExceptedValueWhenGivenValidInput(int input, Func<int, bool> func)
{
var result = Guard.Against.NullOrInvalidInput(input, nameof(input), func);
Assert.Equal(input, result);
}

[Theory]
[InlineData(null, "Input parameterName did not satisfy the options (Parameter 'parameterName')", typeof(ArgumentException), 10)]
[InlineData("Evaluation failed", "Evaluation failed (Parameter 'parameterName')", typeof(ArgumentException), 10)]
[InlineData(null, "Value cannot be null. (Parameter 'parameterName')", typeof(ArgumentNullException))]
[InlineData("Please provide correct value", "Please provide correct value (Parameter 'parameterName')", typeof(ArgumentNullException))]
public void ErrorMessageMatchesExpected(string customMessage, string expectedMessage, Type exceptionType, int? input = null)
{
var exception = Assert.Throws(exceptionType,
() => Guard.Against.NullOrInvalidInput(input, "parameterName", x => x > 20, customMessage));

Assert.NotNull(exception);
Assert.NotNull(exception.Message);
Assert.Equal(expectedMessage, exception.Message);
}

[Theory]
[InlineData(null, null, typeof(ArgumentException), 10)]
[InlineData(null, "Please provide correct value", typeof(ArgumentException), 10)]
[InlineData("SomeParameter", null, typeof(ArgumentNullException))]
[InlineData("SomeOtherParameter", "Value must be correct", typeof(ArgumentNullException))]
public void ExceptionParamNameMatchesExpected(string expectedParamName, string customMessage, Type exceptionType, int? input = null)
{
var exception = Assert.Throws(exceptionType,
() => Guard.Against.NullOrInvalidInput(input, expectedParamName, x => x > 20, customMessage));

Assert.IsAssignableFrom<ArgumentException>(exception);
Assert.NotNull(exception);
Assert.Equal(expectedParamName, (exception as ArgumentException)!.ParamName);
}

public class ValidClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { 20, (Func<int, bool>)(x => x > 10) };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class ArgumentNullExceptionClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { null, (Func<string, bool>)(x => x.Length > 10) };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class ArgumentExceptionClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { "TestData", (Func<string, bool>)(x => x.Length > 10) };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
}