how to manually run Controller FromBody validation - c#

How to run same model validation as it does on controllerValidation validation (using FromBody attribute) ?
I try to do it in my manualValidation endpoint as test example but I don't reach same results
Example: I send payload {"customerNumber": 33,"routingKey": null} to controllerValidation endpoint
and I get this result as expected
{
"errors": {
"routingKey": [
"The RoutingKey field is required."
]
}
}
If I run code shown manualValidation endpoint then it says that model is valid. I don't understand why cause RoutingKey property is null.
Model:
public class TestModel
{
public long CustomerNumber { get; set; }
public string RoutingKey { get; set; } = null!;//Should not be null
}
Controller:
[ApiController]
public class TestController : ControllerBase
{
[HttpPost]
[Route("controllerValidation")]
public TestModel Create([FromBody] TestModel model)
{
//false
return model;
}
}
When I run unit test then I get true as validation result
[TestMethod]
public void TestModel()
{
var model = new TestModel();
model.CustomerNumber = 33;
var context = new ValidationContext(model);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model, context, results, true);//returns true , expect to have false cause RoutingKey is null
Assert.IsFalse(isValid);
}
The only thing how to make it work properly in unit test it to add [Required] attribute to RoutingKey but that's not good solution as I have to do it for all models everywhere as well ...
UPD:this is .NET6

The only thing how to make it work properly in unit test it to add
[Required] attribute to RoutingKey
You can check the TryValidateObject source code, it validate the validation attribute on the property which is why you add the [Required] attribute the test works.
The correct way to judge the ModelState should be:
Test Method:
[TestClass()]
public class WeatherForecastControllerTests
{
private readonly WeatherForecastController controller;
private readonly ILogger<WeatherForecastController> logger;
public WeatherForecastControllerTests()
{
controller = new WeatherForecastController(logger);
}
[TestMethod()]
public void CreateTest()
{
var model = new TestModel();
model.CustomerNumber = 33;
controller.ModelState.AddModelError("RoutingKey", "Required");
// Act
controller.Create(model);
var isValid = controller.ModelState.IsValid;
Assert.IsFalse(isValid);
}
}
Method:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpPost]
[Route("controllerValidation")]
public TestModel Create([FromBody] TestModel model)
{
//false
return model;
}
}
Reference:
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/testing?view=aspnetcore-6.0

Related

C# xUnit Test error - The following constructor parameters did not have matching fixture data

I have implemented a unit test on my ASP.NET Core MVC project using xUnit. When I try to run the test, it gave me an error as below:
The following constructor parameters did not have matching fixture data: Status status"
Below is my code:
IStatusService:
public interface IStatusService
{
Task<StatusIndexViewModel> GetStatusAsync();
}
StatusService:
public class StatusService : IStatusService
{
private readonly DbContext dbContext;
private readonly IMapper mapper;
public StatusService(DbContext dbContext, IMapper mapper)
{
this.dbContext = dbContext;
this.mapper = mapper;
}
public async Task<StatusIndexViewModel> GetStatusAsync()
{
var model = await dbContext
.Status.AsNoTracking()
.ProjectTo<StatusViewModel>(mapper.ConfigurationProvider)
.ToListAsync();
var vm = new StatusIndexViewModel
{
Statuses = model
};
return vm;
}
}
Here is the Controller:
public class StatusController : Controller
{
private readonly IStatusService statusService;
public StatusController(IStatusService statusService)
{
this.statusService = statusService;
}
public async Task<IActionResult> Index()
{
var model = await statusService.GetStatusAsync();
return View(model);
}
}
Below is my unit test class:
public class StatusUnitTest
{
StatusController StatusControllerTest;
private Mock<IStatusService> statusService;
private List<Status> statuses;
private Status status;
public StatusUnitTest(Status status)
{
this.status = status;
statusService = new Mock<IStatusService>();
statusService.Setup(p =>
p.GetStatusAsync()).ReturnsAsync(status);
StatusControllerTest = new StatusController(statusService.Object);
}
[Fact]
public async Task GetStatusByIdNo()
{
var result = await StatusControllerTest.Index();
var viewResult = Assert.IsType<ViewResult>(result);
Assert.IsAssignableFrom<IEnumerable<Status>>
(viewResult.ViewData.Model);
}
}
May I know what mistake I made? How I can test the controller and the service layer? Please give me a guide.

Asp.Net MVC 5 Testing IoC + Dependency Injection

I'm trying to test my project. I have never used tests before and I am starting to learn I would like a help, in the simplest case I want test this public ActionResult Index() but I don't know how to Inject those dependencies.
Controller:
Controller:
public class WorkPlacesController : Controller
{
private readonly IWorkPlaceService workPlaceService;
public WorkPlacesController(IWorkPlaceService workPlaceService)
{
this.workPlaceService = workPlaceService;
}
// GET: WorkPlaces
public ActionResult Index()
{
var workPlaces = workPlaceService.GetWorkPlaces(includedRelated:
true);
return View(workPlaces);
}
}
Here is my Service
Service
public class WorkPlaceService : IWorkPlaceService
{
private readonly IWorkPlaceRepository workPlacesRepository;
private readonly IUnitOfWork unitOfWork;
public WorkPlaceService(IWorkPlaceRepository workPlacesRepository, IUnitOfWork unitOfWork)
{
this.workPlacesRepository = workPlacesRepository;
this.unitOfWork = unitOfWork;
}
}
public interface IWorkPlaceService
{
IEnumerable<WorkPlace> GetWorkPlaces(string workPlaceDescription = null, bool includedRelated = true);
}
And my Repository
Repository
public class WorkPlaceRepository : RepositoryBase<WorkPlace>, IWorkPlaceRepository
{
public WorkPlaceRepository(IDbFactory dbFactory)
: base(dbFactory) { }
public WorkPlace GetWorkPlaceByDescription(string workPlaceDescription)
{
var workPlace = this.DbContext.WorkPlaces.Where(c => c.Description == workPlaceDescription).FirstOrDefault();
return workPlace;
}
}
public interface IWorkPlaceRepository : IRepository<WorkPlace>
{
WorkPlace GetWorkPlaceByDescription(string workPlaceDescription);
}
Factory
public class DbFactory : Disposable, IDbFactory
{
AgendaEntities dbContext;
public AgendaEntities Init()
{
return dbContext ?? (dbContext = new AgendaEntities());
}
protected override void DisposeCore()
{
if (dbContext != null)
dbContext.Dispose();
}
}
I tried to do something like this:
public void BasicIndexTest()
{
// Arrange
var mockRepository = new Mock<IWorkPlaceService>();
var controller = new WorkPlacesController(mockRepository.Object);
// Act
ActionResult actionResult = controller.Index() as ViewResult;
// Assert
Assert.IsInstanceOfType(actionResult, typeof(List<WorkPlace>));
}
How do I inject in this controller the data needed to go in the database and bring the results?
I Want test this public ActionResult Index() but I don't know how to Inject those dependencies.
Mock the behavior of required dependencies of the controller for the test and assert the desired behavior when the test is exercised.
For example, based on what you have done so far
public void BasicIndexTest() {
// Arrange
var mockService = new Mock<IWorkPlaceService>();
var workPlaces = new List<WorkPlace>() {
new WorkPlace()
};
mockService
.Setup(_ => _.GetWorkPlaces(It.IsAny<string>(), It.IsAny<bool>()))
.Returns(workPlaces);
var controller = new WorkPlacesController(mockService.Object);
// Act
var actionResult = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(actionResult);
var model = actionResult.Model;
Assert.IsNotNull(model)
Assert.IsInstanceOfType(model, typeof(List<WorkPlace>));
Assert.AreEqual(workPlaces, model);
}
Only the IWorkPlaceService was needed for the testing of Index action, but fake data was needed for the invocation of the GetWorkPlaces method. So the mock was configured to return a list of objects when called and pass it to the view result.

How to mock Automapper (IMapper) in controller

I am trying to write a unit test for my existing MVC Web Aplication. In that I am facing some problem in automapper (IMapper) Whenever am using map function it returns null value.
My Controller Code:
public class UserAdministrationController : BaseController
{
private readonly iUserService _userService;
private readonly IMapper _mapper;
public NewsController(iUserService userService, IMapper mapper)
{
_userService = userService;
_mapper = mapper;
}
public ActionResult Create(int CompanyID == 0)
{
UserDetail data = _userService(CompanyID);
var Modeldata = _mapper.Map<UserDetailViewModel, UserDetail>(data);
return View(Modeldata);
}
}
Mock Mapping Code:
public class MappingDataTest : CommonTestData
{
public Mock<IMapper> MappingData()
{
var mappingService = new Mock<IMapper>();
UserDetailViewModel interview = getUserDetailViewModel(); // get value of UserDetailViewModel
UserDetail im = getUserDetail(); // get value of UserDetails
mappingService.Setup(m => m.Map<UserDetail, UserDetailViewModel>(im)).Returns(interview);
mappingService.Setup(m => m.Map<UserDetailViewModel, UserDetail>(interview)).Returns(im);
return mappingService;
}
}
Mocking Code:
[TestClass]
public class UserAdminControllerTest
{
private MappingDataTest _common;
[TestInitialize]
public void TestCommonData()
{
_common = new MappingDataTest();
}
[TestMethod]
public void UserCreate()
{
//Arrange
UserAdministrationController controller = new UserAdministrationController(_common.mockUserService().Object, _common.MappingData().Object);
controller.ControllerContext = _common.GetUserIdentity(controller);
// Act
ViewResult newResult = controller.Create() as ViewResult;
// Assert
Assert.IsNotNull(newResult);
}
}
Mapper is not working its always showing the null value in controller. kindly help me. Thanks in Advance.
I would recommend not mocking AutoMapper. There's not much value in controller unit tests for one, and this is similar to mocking a JSON serializer. Just use the real thing.
You should try the following:
public class MappingDataTest : CommonTestData
{
public Mock<IMapper> MappingData()
{
var mappingService = new Mock<IMapper>();
UserDetail im = getUserDetail(); // get value of UserDetails
mappingService.Setup(m => m.Map<UserDetail, UserDetailViewModel>(It.IsAny<UserDetail>())).Returns(interview); // mapping data
mappingService.Setup(m => m.Map<UserDetailViewModel, UserDetail>(It.IsAny<UserDetailtViewModel>())).Returns(im); // mapping data
return mappingService;
}
}
The thing is, your mock was expecting the exact instance of UserDetailViewModel interview = getUserDetailViewModel(); to setup this mapping, and this is why it was returning null. Null it will be expecting any reference to UserDetailViewModel and for any reference to UserDetailtViewModel it will return the expected mapped instance.

FluentValidation not using my Rules

I'm using FluentValidation with Autofac and ValidatorFactoryBase
When I execute my project my Validator is executed, but when I send a post my rules not is used but the current validator is my own Validator.
My Validator:
public class UsuarioCadastrarValidator : AbstractValidator<UsuarioCadastrarVM>
{
public UsuarioCadastrarValidator()
{
RuleFor(a => a.Nome).NotEmpty().WithMessage("Campo obrigatório");
RuleFor(a => a.Nome).Length(4, 200).WithMessage("Digite seu nome completo");
}
}
My Model:
public class UsuarioCadastrarVM
{
public string Nome { get; set; }
public int CargoId { get; set; }
}
Global.asax(Works well):
...
FluentValidationModelValidatorProvider.Configure();
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Validator"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(assembly);
builder
.RegisterType<FluentValidation.Mvc.FluentValidationModelValidatorProvider>()
.As<ModelValidatorProvider>();
builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
...
Controller(Works well):
[HttpPost]
public ActionResult Cadastrar(UsuarioCadastrarVM vm)
{
if(ModelState.IsValid)
{
}
}
My ValidatorFactoryBase (Works well):
public class AutofacValidatorFactory : ValidatorFactoryBase
{
private readonly IComponentContext _context;
public AutofacValidatorFactory(IComponentContext context)
{
_context = context;
}
public override IValidator CreateInstance(Type validatorType)
{
object instance;
if (_context.TryResolve(validatorType, out instance))
{
var validator = instance as IValidator;
return validator;
}
return null;
}
}
When I send Post with "Nome" and "CargoId" empty in ModelState has only one message "CargoId is required" and not exists that Rule, I think is because CargoId is a integer.
But, Why my Rules are not consider?
The problem was CargoId is a integer, so the MVC is not able to bind my post to my ViewModel, because in my tests I sended a empty value, if I send a value to CargoId or change to nullable (int?) the validation works well.

How to Unit Test a controller that dependends on HttpContext

I want to know how to unit test my controller when it inherits from a base controller that is dependent on HttpContext. Below is my inherited controller called BaseInterimController. And below that is the AccountController method that I wish to Unit Test. We are using MOQ.
public abstract class BaseInterimController : Controller
{
#region Properties
protected string InterimName
{
get { return MultiInterim.GetInterimName(InterimIdentifier); }
}
internal virtual string InterimIdentifier
{
get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); }
}
}
public class AccountController : BaseInterimController
{
[HttpPost]
[AllowAnonymous]
[ValidateInput(false)]
[Route(#"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")]
public ActionResult Signin(LoginViewModel model)
{
if (ModelState.IsValid)
{
var identity = Authentication.SignIn(model.Username,
model.Password) as LegIdentity;
if (identity != null && identity.IsAuthenticated)
{
return Redirect(model.ReturnUrl);
}
else
{
// Sign in failed
ModelState.AddModelError("",
Authentication.ExternalSignInFailedMessage);
}
}
return View(model);
}
}
Coupling your controller to HttpContext can make your code very difficult to test because during unit tests HttpContext is null unless you try to mock it; which you shouldn't really do. Don't mock code you don't own.
Instead try abstracting the functionality you want to get from HttpContext into something you have control over.
this is just an example. You can try to make it even more generic if needed. I will focus on your specific scenario.
You are calling this directly in your controller
System.Web.HttpContext.Current.Request
.RequestContext.RouteData.Values["InterimIdentifier"].ToString();
When what you are really after is the ability to get that InterimIdentifier value. Something like
public interface IInterimIdentityProvider {
string InterimIdentifier { get; }
}
public class ConcreteInterimIdentityProvider : IInterimIdentityProvider {
public virtual string InterimIdentifier {
get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); }
}
}
which can later be implemented in a concrete class and injected into your controller provided you are using Dependency Injection.
Your base controller will then look like
public abstract class BaseInterimController : Controller {
protected IInterimIdentityProvider identifier;
public BaseInterimController(IInterimIdentityProvider identifier) {
this.identifier = identifier;
}
protected string InterimName {
get { return MultiInterim.GetInterimName(identifier.InterimIdentifier); }
}
//This can be refactored to the code above or use what you had before
//internal virtual string InterimIdentifier {
// get { return identifier.InterimIdentifier; }
//}
}
public class AccountController : BaseInterimController
{
public AccountController(IInterimIdentityProvider identifier)
: base(identifier){ }
[HttpPost]
[AllowAnonymous]
[ValidateInput(false)]
[Route(#"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")]
public ActionResult Signin(LoginViewModel model)
{
if (ModelState.IsValid)
{
var identity = Authentication.SignIn(model.Username,
model.Password) as LegIdentity;
if (identity != null && identity.IsAuthenticated)
{
return Redirect(model.ReturnUrl);
}
else
{
// Sign in failed
ModelState.AddModelError("",
Authentication.ExternalSignInFailedMessage);
}
}
return View(model);
}
}
This allows implemented controllers to not be dependent on HttpContext which will allow for better unit testing as you can easily mock/fake IInterimIdentityProvider interface using Moq to return what you want during tests.
[TestMethod]
public void Account_Controller_Should_Signin() {
//Arrange
var mock = new Mock<IInterimIdentityProvider>();
mock.Setup(m => m.InterimIdentifier).Returns("My identifier string");
var controller = new AccountController(mock.Object);
var model = new LoginViewModel() {
Username = "TestUser",
Password = ""TestPassword
};
//Act
var actionResult = controller.Signin(model);
//Assert
//...assert your expected results
}

Categories