Skip to content

Instantly share code, notes, and snippets.

@danbarua
Created May 20, 2014 13:11
Show Gist options
  • Save danbarua/f450a89f33c92d11e412 to your computer and use it in GitHub Desktop.
Save danbarua/f450a89f33c92d11e412 to your computer and use it in GitHub Desktop.
Testing FluentValidators with Nancy (appdomain scanning bypassed)
namespace Nancy.Validation.FluentValidation.Tests
{
using System;
using System.Collections.Generic;
using System.Linq;
using Nancy.Bootstrapper;
using Nancy.ModelBinding;
using Nancy.Testing;
using Nancy.TinyIoc;
using Xunit;
using global::FluentValidation;
public interface IFooQueryHandler
{
bool Exists(string fooName);
}
public class FakeQueryHandler : IFooQueryHandler
{
public bool Exists(string fooName)
{
return true;
}
}
public class CreateFoo
{
public string Name { get; set; }
}
public class CreateBar
{
public string Name { get; set; }
}
public class ValidatorWithDependency : AbstractValidator<CreateFoo>
{
public ValidatorWithDependency(IFooQueryHandler query)
{
this.RuleFor(x => x.Name).Must((req, name) => !query.Exists(name)).WithMessage("Philip says this is a code smell but I don't care.");
}
}
public class ValidatorWithNoDependency : AbstractValidator<CreateBar>
{
public ValidatorWithNoDependency()
{
this.RuleFor(x => x.Name).NotEmpty().NotNull();
}
}
public class DummyModule : NancyModule
{
public DummyModule()
{
Post["/create/bar"] = _ =>
{
var model = this.BindAndValidate<CreateBar>();
return !this.ModelValidationResult.IsValid ? 400 : 200;
};
Post["/create/foo"] = _ =>
{
var model = this.BindAndValidate<CreateFoo>();
return !this.ModelValidationResult.IsValid ? 400 : 200;
};
}
}
public class TestBootstrapper : ConfigurableBootstrapper
{
public TestBootstrapper(Action<ConfigurableBootstrapperConfigurator> configuration)
: base(configuration)
{
}
protected override IEnumerable<Type> ApplicationRegistrationTasks
{
get
{
//remove the default FluentValidationRegistrations
var tasks = base.ApplicationRegistrationTasks.Where(x => x != typeof(FluentValidation.Registrations));
return tasks;
}
}
public class CustomFluentRegistrations : ApplicationRegistrations
{
//this will get picked up by the appdomain scanner
public CustomFluentRegistrations()
{
this.Register<IModelValidator>(typeof(FluentValidationValidator));
this.Register<IFluentAdapterFactory>(typeof(DefaultFluentAdapterFactory));
this.RegisterAll<IFluentAdapter>();
this.Register<IModelValidatorFactory>(typeof(CustomFluentValidationValidatorFactory));
}
}
//fallback factory will pull validators out of the container
public class CustomFluentValidationValidatorFactory : IModelValidatorFactory
{
private readonly IFluentAdapterFactory adapterFactory;
private readonly TinyIoCContainer container;
public CustomFluentValidationValidatorFactory(IFluentAdapterFactory adapterFactory, TinyIoc.TinyIoCContainer container)
{
this.adapterFactory = adapterFactory;
this.container = container;
}
public IModelValidator Create(Type type)
{
var instance =
GetValidatorInstance(type);
return (instance != null) ?
new FluentValidationValidator(instance, this.adapterFactory, type) :
null;
}
private IValidator GetValidatorInstance(Type type)
{
var fullType =
CreateValidatorType(type);
return (IValidator)container.Resolve(fullType);
}
private static Type CreateValidatorType(Type type)
{
return typeof(AbstractValidator<>).MakeGenericType(type);
}
}
}
public class ValidatorWithDependencyTests
{
[Fact]
public void Should_throw_a_wobbly_if_i_forget_to_register_a_validator()
{
// arrange
var bootstrapper = new TestBootstrapper(
with =>
{
with.Module<DummyModule>();
});
var browser = new Browser(bootstrapper);
// act
var ex = Record.Exception(
() => browser.Post(
"create/bar",
with =>
{
with.HttpRequest();
with.FormValue("Name", string.Empty);
}));
// assert
Assert.NotNull(ex);
Assert.True(ex.Message.StartsWith("ConfigurableBootstrapper Exception"));
}
[Fact]
public void Should_be_able_to_test_validators_registered_as_dependencies()
{
// arrange
var bootstrapper = new TestBootstrapper(
with =>
{
with.Module<DummyModule>();
with.Dependencies(new ValidatorWithNoDependency());
});
var browser = new Browser(bootstrapper);
// act
var response = browser.Post(
"create/bar",
with =>
{
with.HttpRequest();
with.FormValue("Name", string.Empty);
});
//assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public void should_be_able_to_test_validators_with_instance_dependencies()
{
var bootstrapper = new TestBootstrapper(
with =>
{
with.Module<DummyModule>();
//Yuck! ConfigurableBootstrapper needs a change to allow registering a class as its base(generic) class
with.Dependencies<AbstractValidator<CreateFoo>>(new ValidatorWithDependency(new FakeQueryHandler()));
});
var browser = new Browser(bootstrapper);
var response = browser.Post(
"create/foo",
with =>
{
with.HttpRequest();
with.FormValue("Name", "Test Name");
});
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment