FluentValidation: Mocking ValidationResult.IsValid - .net-core

I'm using FluentValidation with WebAPI in DotNet core 2. I've written tests for the validator successfully, but now I'm trying to mock the validator for my controller. Controller as follows:
[Route("[controller]")]
public class SecurityController : Controller {
private readonly IValidator<AuthenticateRequest> _authenticateRequestValidator;
public SecurityController(IValidator<AuthenticateRequest> authenticateRequestValidator) {
_authenticateRequestValidator = authenticateRequestValidator;
}
[HttpPost]
[AllowAnonymous]
[Route("auth")]
public async Task<IActionResult> AuthenticateAsync([FromBody] AuthenticateRequest req) {
// Validate
var validator = await _authenticateRequestValidator.ValidateAsync(req);
if(!validator.IsValid) {
return BadRequest();
}
// ...snip
}
}
AuthenticateRequest looks like this:
public class AuthenticateRequest {
public string Username { get; set; }
public string Password { get; set; }
}
And the validator is as follows:
public class AuthenticateRequestValidator : AbstractValidator<AuthenticateRequest> {
/// <summary>
/// Provides a validator for <see cref="AuthenticateRequest" />
/// </summary>
public AuthenticateRequestValidator() {
RuleFor(x => x.Username)
.NotNull()
.NotEmpty()
.WithMessage("Username is required");
RuleFor(x => x.Password)
.NotNull()
.NotEmpty()
.WithMessage("Password is required");
}
}
It's being injected into the controller with dot net core's standard DI. Not posting code as it's not relevant to this issue, as its a testing issue.
I'm testing with xunit, Moq and AutoFixture. Here are two tests:
public class SecurityControllerTests {
private readonly IFixture Fixture = new Fixture().Customize(new AutoMoqCustomization {ConfigureMembers = true});
private readonly Mock<IValidator<AuthenticateRequest>> authenticateRequestValidatorMock;
public SecurityControllerTests() {
authenticateRequestValidatorMock = Mock.Get(Fixture.Create<IValidator<AuthenticateRequest>>());
}
[Fact]
public async Task Authenticate_ValidatesRequest() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => Fixture.Create<Task<ValidationResult>>())
.Verifiable();
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
await controller.AuthenticateAsync(request);
// Assert
authenticateRequestValidatorMock.Verify();
}
[Fact]
public async Task Authenticate_Returns400_WhenUsernameValidationFails() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
var validationResultMock = new Mock<ValidationResult>();
validationResultMock
.SetupGet(x => x.IsValid)
.Returns(() => true);
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => new Task<ValidationResult>(() => validationResultMock.Object));
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
var result = await controller.AuthenticateAsync(request);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
}
I need to mock ValidationResult so I can ignore the actual validator logic (which is tested elsewhere) and test the controller logic. There are many other dependencies injected, and much more code, but the pasted code is the crux of the problem and produces the same results when everything else is stripped out.
First test passes, second runs forever when it hits the var validator = await _authenticateRequestValidator.ValidateAsync(req); line in the controller.
Its worth noting that ValidationResult.IsValid is a virtual readonly property.
What is wrong with the second test?

Did you try Asp.Net Core - FluentValidation integration? By this way you dont need to pass Validator depedencies to constructor.
https://github.com/JeremySkinner/FluentValidation/wiki/i.-ASP.NET-Core-integration
FluentValidation fills ModelState in case of validation error and you use it like ;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
For testing it you set ModelState of you Controllers Mock
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
controller.ModelState.AddModelError("test", "test");
// Act
IActionResult actionResult = await controller.AuthenticateAsync(request);
var badRequestObjectResult = actionResult as BadRequestObjectResult;
Assert.NotNull(badRequestObjectResult);
var serializableError = badRequestObjectResult.Value as SerializableError;
// Assert
Assert.NotNull(result);
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
var serializableError = assert.IsType<SerializableError>(badRequestResult.Value)
Assert.True(((string[])serializableError["test"])[0] == "test");
Leaving ModelState empty would be enough to ignore the actual validator logic i think.
Also FluentValidation have built-in testing api. You can test your validation logic separately.
https://github.com/JeremySkinner/FluentValidation/wiki/g.-Testing

Related

Configure MassTransit for testing with WebApplicationFactory<Startup>

I have an ASP.NET Core web app and test setup using WebApplicationFactory to test my controller actions. I used RawRabbit before and it was easy enough for me to mock the IBusClient and add it to the DI container as a singleton. Within the WebApplicationFactory<TStartup>.CreateWebHostBuilder() I call this extension method to add my mocked IBusClient instance like so;
/// <summary>
/// Configures the service bus.
/// </summary>
/// <param name="webHostBuilder">The web host builder.</param>
/// <returns>A web host builder.</returns>
public static IWebHostBuilder ConfigureTestServiceBus(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.ConfigureTestServices(services =>
{
services.AddSingleton<IBusClient, MY_MOCK_INSTANCE>
});
return webHostBuilder;
}
But there are gaps in RawRabbit right now that made me decide to move over to MassTransit. However, I am wondering if there's already a better way to register the IBus into my container without mocking it inside my test. Not sure if InMemoryTestFixture, BusTestFixture, or BusTestHarness is the solution to my problem. Not sure how to use them together and what they do.
By the way, in my ASP.NET Core app, I have a reusable extension method setup like the code below to hook me up to RabbitMQ on startup.
/// <summary>
/// Adds the service bus.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="configurator">The configurator.</param>
/// <returns>A service collection.</returns>
public static IServiceCollection AddServiceBus(this IServiceCollection services, Action<IServiceCollectionConfigurator> configurator)
{
var rabbitMqConfig = new ConfigurationBuilder()
.AddJsonFile("/app/configs/service-bus.json", optional: false, reloadOnChange: true)
.Build();
// Setup DI for MassTransit.
services.AddMassTransit(x =>
{
configurator(x);
// Get the json configuration and use it to setup connection to RabbitMQ.
var rabbitMQConfig = rabbitMqConfig.GetSection(ServiceBusOptionsKey).Get<RabbitMQOptions>();
// Add bus to the container.
x.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(
new Uri(rabbitMQConfig.Host),
hostConfig =>
{
hostConfig.Username(rabbitMQConfig.Username);
hostConfig.Password(rabbitMQConfig.Password);
hostConfig.Heartbeat(rabbitMQConfig.Heartbeat);
});
cfg.ConfigureEndpoints(provider);
// Add Serilog logging.
cfg.UseSerilog();
}));
});
// Add the hosted service that starts and stops the BusControl.
services.AddSingleton<IMessageDataRepository, EncryptedMessageDataRepository>();
services.AddSingleton<IEndpointNameFormatter, EndpointNameFormatter>();
services.AddSingleton<IBus>(provider => provider.GetRequiredService<IBusControl>());
services.AddSingleton<IHostedService, BusHostedService>();
return services;
}
A MassTransit config defined during Startup could be replaced with a new configuration with custom WebApplicationFactory by removing services from MassTransit namespace, e.g.
public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var massTransitHostedService = services.FirstOrDefault(d => d.ServiceType == typeof(IHostedService) &&
d.ImplementationFactory != null &&
d.ImplementationFactory.Method.ReturnType == typeof(MassTransitHostedService)
);
services.Remove(massTransitHostedService);
var descriptors = services.Where(d =>
d.ServiceType.Namespace.Contains("MassTransit",StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var d in descriptors)
{
services.Remove(d);
}
services.AddMassTransitInMemoryTestHarness(x =>
{
//add your consumers (again)
});
});
}
}
Then your test could look like
public class TestClass : IClassFixture<CustomApplicationFactory>
{
private readonly CustomApplicationFactoryfactory;
public TestClass(CustomApplicationFactoryfactory)
{
this.factory = factory;
}
[Fact]
public async Task TestName()
{
CancellationToken cancellationToken = new CancellationTokenSource(5000).Token;
var harness = factory.Services.GetRequiredService<InMemoryTestHarness>();
await harness.Start();
var bus = factory.Services.GetRequiredService<IBusControl>();
try
{
await bus.Publish<MessageClass>(...some message...);
bool consumed = await harness.Consumed.Any<MessageClass>(cancellationToken);
//do your asserts
}
finally
{
await harness.Stop();
}
}
}
I ended up creating a method from within my WebApplicationFactory like so;
public void ConfigureTestServiceBus(Action<IServiceCollectionConfigurator> configurator)
{
this._configurator = configurator;
}
Giving me the ability to register test handlers from within my derived integration class constructor;
public Intg_GetCustomers(WebApplicationTestFactory<Startup> factory)
: base(factory)
{
factory.ConfigureTestServiceBus(c =>
{
c.AddConsumer<TestGetProductConsumer>();
});
}
This configurator gets used when I call my extension method to add an InMemory instance of MassTransit
public static IWebHostBuilder ConfigureTestServiceBus(this IWebHostBuilder webHostBuilder, Action<IServiceCollectionConfigurator> configurator)
{
return webHostBuilder
.ConfigureTestServices(services =>
{
// UseInMemoryServiceBus DI for MassTransit.
services.AddMassTransit(c =>
{
configurator?.Invoke(c);
// Add bus to the container.
c.AddBus(provider =>
{
var control = Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ConfigureEndpoints(provider);
});
control.Start();
return control;
});
});
services.AddSingleton<IMessageDataRepository, InMemoryMessageDataRepository>();
});
}
You best bet is to use the InMemoryTestHarness, so that you can ensure your message contracts can be serialized, your consumers are configured properly, and that everything works as expected. While some might call this an integration test, it's really just doing a proper test. And it's extremely quick, since it's all in-memory.
You can see a unit test here, but a short example is also shown below.
[TestFixture]
public class When_a_consumer_is_being_tested
{
InMemoryTestHarness _harness;
ConsumerTestHarness<Testsumer> _consumer;
[OneTimeSetUp]
public async Task A_consumer_is_being_tested()
{
_harness = new InMemoryTestHarness();
_consumer = _harness.Consumer<Testsumer>();
await _harness.Start();
await _harness.InputQueueSendEndpoint.Send(new A());
}
[OneTimeTearDown]
public async Task Teardown()
{
await _harness.Stop();
}
[Test]
public void Should_have_called_the_consumer_method()
{
_consumer.Consumed.Select<A>().Any().ShouldBe(true);
}
class Testsumer :
IConsumer<A>
{
public async Task Consume(ConsumeContext<A> context)
{
await context.RespondAsync(new B());
}
}
class A
{
}
class B
{
}
}
In my case (I'm injecting everywhere IPublishEndpoint interface only) I have just simply registered another IPublishEndpoint in ConfigureTestServices method like so:
[TestClass]
public class TastyTests
{
private readonly WebApplicationFactory<Startup> factory;
private readonly InMemoryTestHarness harness = new();
public TastyTests()
{
factory = new WebApplicationFactory<Startup>().WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IPublishEndpoint>(serviceProvider =>
{
return harness.Bus;
});
});
});
}
[TestMethod]
public async Task Test()
{
await harness.Start();
try
{
var client = factory.CreateClient();
const string url = "/endpoint-that-publish-message";
var content = new StringContent("", Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
(await harness.Published.Any<IMessage>()).Should().BeTrue();
}
finally
{
await harness.Stop();
}
}
}

Testing razor pages app

I am writing a Razor Pages app for a University project, which I am required to test. I couldn't find many sources and examples online on testing Razor Pages and I'm trying to follow the examples on this link : https://learn.microsoft.com/en-us/aspnet/core/testing/razor-pages-testing?view=aspnetcore-2.1
My first problem is unit testing:
This is the test method I wrote, it's supposed to check that a value that is filled in the OnGet method on my model is receiving the correct value:
[Fact]
public void OnGet_ViewStores()
{
// Arrange
var httpContext = new DefaultHttpContext();
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
var modelMetadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
var pageContext = new PageContext(actionContext)
{
ViewData = viewData
};
var storesModel = new StoresModel()
{
PageContext = pageContext,
Url = new UrlHelper(actionContext)
};
#region snippet2
// Act
storesModel.OnGet();
#endregion
#region snippet3
// Assert
var actualStores = wsep1.Services.ViewAllStores(1);
Assert.Equal(storesModel.StoreDetails, actualStores);
#endregion
}
And this is the model which is being checked:
public class StoresModel : PageModel
{
public List<string> StoreDetails { get; set; }
public string Message { get; set; }
public int clientId;
public void OnGet()
{
clientId = (int)HttpContext.Session.GetInt32("clientId");
Message = "Your clientId id is " + clientId;
StoreDetails = wsep1.Services.ViewAllStores(clientId);
}
}
The problem is that the test throws an exception because I am using an HttpContext.Session which is not configures properly in the test. In my real project it is configured beforehand in Startup.cs in this method:
public void ConfigureServices(IServiceCollection services)
{
services.AddWebSocketManager();
services.AddMvc();
services.AddDistributedMemoryCache();
services.AddTransient<ShoppingHandler>();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(1000);
options.Cookie.HttpOnly = true;
});
}
But I can't seem to find a way to configure this in my test.
My second problem is with integration testing:
I am trying to run a very basic test with the Test Server, this is my test class:
public class IndexPageTest : IClassFixture<TestFixture<Client.Startup>>
{
private readonly HttpClient _client;
public IndexPageTest(TestFixture<Client.Startup> fixture)
{
_client = fixture.Client;
}
#region snippet1
[Fact]
public async Task Request_ReturnsSuccess()
{
// Act
var response = await _client.GetAsync("/");
// Assert
response.EnsureSuccessStatusCode();
}
#endregion
}
I hardly changed the TextFixture class that was included in the demo project in the link I gave at the beginning of the post, all I did was add my services to the configuration method (as I said before, I'm using a Session object and also WebSocketManager in my app).
_client.GetAsync("/") returns a status of "500 - internal server error" and I have no idea why and how to configure these tests to work.
Any ideas would be appreciated, Thanks.

How do I test an ActionFilterAttribute with ASP.NET Web API?

I'm building an ASP.NET Web API that uses a very simple action filter:
public class ValidateModelAttribute : ActionFilterAttribute {
public override void OnActionExecuting(HttpActionContext actionContext) {
if (actionContext.ModelState.IsValid == false) {
var responseObject = new ApiResponse() {
Errors = actionContext.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage)
};
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, responseObject);
}
}
}
My action filter simply enforces that the ModelState is valid; if not, it returns a 400 with all the error messages so the client can do whatever they wish with the data.
Here is how I'm using the attribute in my controller:
[ValidateModelAttribute]
public HttpResponseMessage Db(DbModel model) {
return Request.CreateResponse(HttpStatusCode.OK);
}
I'm trying to test the attribute using Visual Studio's testing framework:
[TestMethod]
public void CaptureApiDatabase_IfRequiredFieldMissing_ReturnHttpBadRequest() {
var controller = new ConfigureController();
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
var validationAttribute = new ValidateModelAttribute();
validationAttribute.OnActionExecuting(controller.ActionContext);
Assert.IsTrue(controller.ActionContext.Response.StatusCode == HttpStatusCode.BadRequest);
}
This isn't working because I haven't provided the filter my data and as a result the ModelState is always valid. How do I build a context so I can test this filter attribute?

Change applied in database but DbContext does not show

I have an asp.net webapi hosted using OWin and I use ninject for DI. Here is how I inject DbContext:
kernel.Bind<IAuthRepository>().To<AuthRepository>();
kernel.Bind<AuthDbContext>().ToSelf().InRequestScope();
I have a repository class that I use the context as below:
private AuthDbContext _ctx { get; set; }
public AuthRepository(AuthDbContext ctx)
{
_ctx = ctx;
}
There is this method in repository that I call to change some data:
var res = await _ctx
.Clients
.FirstOrDefaultAsync(clientEntity => clientEntity.UserRef.Id == p);
//do the modification
_ctx.Entry(res).State = EntityState.Modified;
_ctx.SaveChanges();
return res;
It saves the data correctly in db, but after, when I call another action and get that saved entity, I see that changes are not in the context.
public async Task<Client> FindClientById(Guid clientId)
{
return await _ctx
.Clients
.FirstOrDefaultAsync(clientEntity => clientEntity.Id == clientId);
}

Unit test controller - membership error

I want to create a Unit test for the following controller but it got fail in the Membership class:
public class AccountController:BaseController
{
public IFormsAuthenticationService FormsService { get; set; }
public IMembershipService MembershipService { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if(FormsService == null) { FormsService = new FormsAuthenticationService(); }
if(MembershipService == null) { MembershipService = new AccountMembershipService(); }
base.Initialize(requestContext);
}
public ActionResult LogOn()
{
return View("LogOn");
}
[HttpPost]
public ActionResult LogOnFromUser(LappLogonModel model, string returnUrl)
{
if(ModelState.IsValid)
{
string UserName = Membership.GetUserNameByEmail(model.Email);
if(MembershipService.ValidateUser(model.Email, model.Password))
{
FormsService.SignIn(UserName, true);
var service = new AuthenticateServicePack();
service.Authenticate(model.Email, model.Password);
return RedirectToAction("Home");
}
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View("LogOn", model);
}
}
Unit test code:
[TestClass]
public class AccountControllerTest
{
[TestMethod]
public void LogOnPostTest()
{
var mockRequest = MockRepository.GenerateMock();
var target = new AccountController_Accessor();
target.Initialize(mockRequest);
var model = new LogonModel() { UserName = "test", Password = "1234" };
string returnUrl = string.Empty;
ActionResult expected = null;
ActionResult actual = target.LogOn(model, returnUrl);
if (actual == null)
Assert.Fail("should have redirected");
}
}
When I googled, I got the following code but I don't know how to pass the membership to the accountcontroller
var httpContext = MockRepository.GenerateMock();
var httpRequest = MockRepository.GenerateMock();
httpContext.Stub(x => x.Request).Return(httpRequest);
httpRequest.Stub(x => x.HttpMethod).Return("POST");
//create a mock MembershipProvider & set expectation
var membershipProvider = MockRepository.GenerateMock();
membershipProvider.Expect(x => x.ValidateUser(username, password)).Return(false);
//create a stub IFormsAuthentication
var formsAuth = MockRepository.GenerateStub();
/*But what to do here???{...............
........................................
........................................}*/
controller.LogOnFromUser(model, returnUrl);
Please help me to get this code working.
It appears as though you are using concrete instances of the IMembershipServive and IFormsAuthenticationService because you are using the Accessor to initialize them. When you use concrete classes you are not really testing this class in isolation, which explains the problems you are seeing.
What you really want to do is test the logic of the controller, not the functionalities of the other services.
Fortunately, it's an easy fix because the MembershipService and FormsService are public members of the controller and can be replaced with mock implementations.
// moq syntax:
var membershipMock = new Mock<IMembershipService>();
var formsMock = new Mock<IFormsAuthenticationService>();
target.FormsService = formsMock.Object;
target.MembershipService = membershipService.Object;
Now you can test several scenarios for your controller:
What happens when the MembershipService doesn't find the user?
The password is invalid?
The user and password is is valid?
Note that your AuthenticationServicePack is also going to cause problems if it has additional services or dependencies. You might want to consider moving that to a property of the controller or if it needs to be a single instance per authentication, consider using a factory or other service to encapsuate this logic.

Resources