Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not sure how to test this .NET string with FluentValidation

REF: A .NET Fiddle of (basically) the code, below.

I'm trying to test if a string is a valid Uri using FluentValidation:

public class LinksValidator : AbstractValidator<string>
{
    public LinksValidator()
    {
        RuleFor(x => x)
            .Must(LinkMustBeAUri)
            .WithMessage("Link '{PropertyValue}' must be a valid URI. eg: http://www.SomeWebSite.com.au");
    }

    private static bool LinkMustBeAUri(string link)
    {
        if (string.IsNullOrWhiteSpace(link))
        {
            return false;
        }

        Uri result;
        return Uri.TryCreate(link, UriKind.Absolute, out result);
    }
}

and for the validation test...

public class LinksValidatorTests
{
    private readonly LinksValidator _linksValidator;

    public LinksValidatorTests()
    {
        _linksValidator = new LinksValidator();    
    }

    [Theory]
    [InlineData("Http://www.SomeDomain.com")]
    [InlineData("https://www.SomeDomain.com")]
    [InlineData("http://www.SomeDomain.com.au")]
    public void GivenAValidUri_Validate_ShouldNotHaveAValidationError(string uri)
    {
        // Arrange.

        // Act & Assert.
        _linksValidator.ShouldNotHaveValidationErrorFor(l => l, uri);
    }
}

but the last line in that test method doesn't compile:

enter image description here

How can I provide a hint to the compiler to tell it which method to use?

like image 639
Pure.Krome Avatar asked Feb 08 '23 07:02

Pure.Krome


1 Answers

So, it's not possible since it's within a library you can't modify. See here.

There is a work-around, however. And that's to make sure that the generic arguments are not the same.

Here is working code:

public static void Main()
{
    Console.WriteLine("Hello World");

    var uri = "Http://www.SomeDomain.com";
    var linksValidator = new LinksValidator();
    linksValidator.ShouldNotHaveValidationErrorFor(uri);
}

public class LinksValidator : AbstractValidator<LinksValidator.UrlWrapper>
{
    public class UrlWrapper { public string Url { get; set; } }
    public LinksValidator()
    {
        RuleFor(x => x.Url)
            .Must(LinkMustBeAUri)
            .WithMessage("Link '{PropertyValue}' must be a valid URI. eg: http://www.SomeWebSite.com.au");
    }

    //Optional 'overload' since l => l.Url seems to be superfluous
    public void ShouldNotHaveValidationErrorFor(string uri)
    {
        this.ShouldNotHaveValidationErrorFor(l => l.Url, uri);
    }

    private static bool LinkMustBeAUri(string link)
    {
        if (string.IsNullOrWhiteSpace(link))
        {
            return false;
        }

        //Courtesy of @Pure.Krome's comment and https://stackoverflow.com/a/25654227/563532
        Uri outUri;
        return Uri.TryCreate(link, UriKind.Absolute, out outUri)
               && (outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps);
    }
}

I'm not too familiar with how the FluentValidation library works - so there may be a tidier way to wrap this hack

Also note, I changed the way you detect Uri strings. TryCreate will return true for just about any string (I was unable to find a case where it was ever false).

like image 98
Rob Avatar answered Feb 16 '23 03:02

Rob