public class MyUser: IIdentity, IMyUser{
// ommited for abbrev.
}
public interface IMyUser
{
int Id { get; set; }
int? CompanyId { get; set; }
}
inside MyController I'm using MyUser besides others to populate
comboboxes
public ActionResult Details(int? subsidId = null, int? req = null)
{
...
MyUser user = this.User.GetInfo();
var obj1 = ... // ommited on purpose for abbrev.
populateCombos(subsidId, user.CompanyId, req);
}
I'm getting exception on this line populateCombos cause user object is always null.
Inside same controller I'm injecting interface which IMyUser implements
[Inject]
public IMyUser MyUser { get; set; }
this property is correctly binded using ninject (like others in my app)
kernel.Bind<IMyUser>().To<MyUser>().InRequestScope();
now on testing project I'm initializing controller with mocking requested dependencies
[SetUp]
public void Setup()
{
_controller = new MyController(){
... repositories....
MyUser = MockMyUser()
}
}
private IMyUser MockMyUser()
{
var u = new Mock<IMyUser>();
u.SetupGet(x => x.Id).Returns(1);
u.SetupGet(x => x.CompanyId).Returns(99);
return u.Object;
}
and inside test method I wrote simple test
[Test]
public void CanDoDetails()
{
ViewResult res = this.controller.Details(1, 2) as ViewResult;
var model = result.Model as MyModel;
Assert.IsNotNull(model);
}
Question is:
why I'm getting this dependency (MyUser inside MyController as
null) cause it's injected properly? What I'm doing wrong?
Update:
public static MyUser GetInfo(this IPrincipal principal)
{
if (principal != null)
{
return principal.Identity as MyUser;
}
return null;
}
Update 2:
Based on Nkosi answer bellow I make following changes
public interface IMyUser : IIdentity { ... }
and inside Details ActionMethod controller
IMyUser user = this.User.GetInfo();
and in test method under
[SetUp]
public void Setup()
{
var mockUser = MockMyUser();
string[] roles = new[] { "Admin", "SuperUser" };
_controller = new MyController()
{
....
MyUser = this.MockMyUser(),
ControllerContext = new ControllerContext() {
Controller = _controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
}
}
}
but I'm still getting `IMyUser user = this.User.GetInfo();`
`this.User` is still null.
p.s. I also changed GetInfo to return IMyUser instead of MyUser.
What about mocking controller also?
Mock<MyController> mockController = new Mock<MyController>();
mockController.SetupGet(t => t.MyUser).Returns(MockMyUser());
then you can access your controller via mockController.Object and try your test.
My advice would also be to make IMyUser inherit from IIdentity
public class MyUser: IMyUser {
// ommited for abbrev.
}
public interface IMyUser: IIdentity {
int Id { get; set; }
int? CompanyId { get; set; }
}
For your unit test to work if you are using the Controller.User { get; } is to create a mock/Fake user for the controller. How ever in order to get access to the User, which is read-only, you have to create a mock HttpContext. yuck. Luckily you only want access to the User anyway.
private class MockHttpContext : HttpContextBase {
private readonly IPrincipal user;
public MockHttpContext(IIdentity identity , string[] roles = null) {
var principal = new GenericPrincipal(identity, roles ?? new string[] { });
user = principal;
}
public override IPrincipal User {
get {
return user;
}
set {
base.User = value;
}
}
}
You can setup the principal to suit your authentication setup with what ever claims you apply at runtime. This is just an example.
[SetUp]
public void Setup()
{
string[] roles = new[] { "Admin", "SuperUser" };
var mockUser = MockMyUser();
_controller = new MyController(){
... repositories....
MyUser = mockUser
};
_controller.ControllerContext = new ControllerContext() {
Controller = _controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
};
}
private IMyUser MockMyUser()
{
var u = new Mock<IMyUser>();
u.Setup(x => x.Name).Returns("username#test.io");
u.Setup(x => x.Id).Returns(1);
u.Setup(x => x.CompanyId).Returns(99);
return u.Object;
}
This should now allow
public static IMyUser GetInfo(this IPrincipal principal) {
if (principal != null) {
return principal.Identity as IMyUser;
}
return null;
}
To return the principal.Identity as IMyUser to not be null.
UPDATE
I recreated a minimal version of your test using what I provided you above and was able to test it and pass.
[TestClass]
public class MyUserDependentControllerTest {
[TestMethod]
public void CanDoDetails() {
//Arrange
string[] roles = new[] { "Admin", "SuperUser" };
var u = new Mock<IMyUser>();
u.Setup(x => x.Name).Returns("username#test.io");
u.Setup(x => x.Id).Returns(1);
u.Setup(x => x.CompanyId).Returns(99).Verifiable();
var mockUser = u.Object;
var controller = new MyController() {
//... repositories....
MyUser = mockUser
};
controller.ControllerContext = new ControllerContext() {
Controller = controller,
RequestContext = new RequestContext(new MockHttpContext(mockUser, roles), new RouteData())
};
//Act
var result = controller.Details(1, 2);
//Assert
var viewResult = result as ViewResult;
Assert.IsNotNull(viewResult);
var model = viewResult.Model as MyModel;
Assert.IsNotNull(model);
u.Verify();
}
public class MyController : Controller {
public ActionResult Details(int? subsidId = null, int? req = null) {
//...
var user = this.User.GetInfo();
//
populateCombos(subsidId, user.CompanyId, req);
//this is just for matching test expectations
var model = new MyModel();
return View(model);
}
private void populateCombos(int? subsidId, int? nullable, int? req) {
//Empty as I have no clue what happens in here
}
public IMyUser MyUser { get; set; }
}
public class MyModel { }
}
Related
I have this action filter that I want to call from a function. However, within this filter I am referring to another class called ValidateRoleClient. How can I do a service injection? Or call this class in my function properly?
public class RolesFilterAttribute : ActionFilterAttribute
{
public ValidateRoleClient validateRoleClient;
public string Role { get; set; }
public RolesFilterAttribute(ValidateRoleClient validateRoleClient)
{
this.validateRoleClient = validateRoleClient;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = Role,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
}
This is how I refer to the filter in my function
[RolesFilter]
public IActionResult About()
{
return View();
}
The current error I get from this is
Severity Code Description Project File Line Suppression
State Error CS7036 There is no argument given that corresponds to
the required formal parameter 'validateRoleClient' of
'RolesFilterAttribute.RolesFilterAttribute(ValidateRoleClient)'
You could wrap your attribute inside a FilterFactory like so:
public class RolesFilterAttribute : Attribute, IFilterFactory
{
public string Role { get; set; }
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return new RolesFilterAttributeImplementation(
serviceProvider.GetRequiredService<ValidateRoleClient>(),
Role
);
}
private class RolesFilterAttributeImplementation : ActionFilterAttribute
{
private readonly ValidateRoleClient validateRoleClient;
private readonly string role;
public RolesFilterAttributeImplementation(ValidateRoleClient validateRoleClient, string role)
{
this.validateRoleClient = validateRoleClient;
this.role = role;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Cookies["Token"] != null || context.HttpContext.Request.Cookies["RefreshToken"] != null)
{
TokenViewModel tvm = new TokenViewModel
{
Token = context.HttpContext.Request.Cookies["Token"],
RefreshToken = context.HttpContext.Request.Cookies["RefreshToken"]
};
ValidateRoleViewModel vrvm = new ValidateRoleViewModel
{
Role = role,
Token = tvm
};
validateRoleClient.ValidateRole(vrvm);
}
}
}
public bool IsReusable => false;
}
Of course the ValidateRoleClient service must first be configured to be injected.
You can then use the RolesFilterAttribute attribute as you normally would.
And you can read more about IFilterFactory here: https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1#ifilterfactory
I want to saw that I do not care what is in the Find clause. Just give me null.
Here is the test
[Fact]
public void VerifyIndexViewType()
{
// Arrange
var mockUserProvider = new Mock<IUserProvider>();
mockUserProvider.Setup(x => x.GetUserId()).Returns("any-value-here");
var mockUnitOfWork = new Mock<IUnitOfWork>();
//how would I just return an object or null for example.. this doesnt work
// mockUnitOfWork.Setup(x => x.UserProfileDataRepository.Find(u => u.ApplicationUserId == "any value here")).Returns((IRepository<UserProfileData>)null);
var controller = new ProfileController(mockUnitOfWork.Object, mockUserProvider.Object);
// Act
var result = controller.Update();
// Assert
Assert.IsType<ViewResult>(result);
}
For the following controller and action
public class ProfileController : BaseController
{
private IUnitOfWork _unitOfWork;
private readonly IUserProvider _requestUserProvider;
public ProfileController(IUnitOfWork unitOfWork,
IUserProvider requestUserProvider)
: base(unitOfWork, requestUserProvider)
{
_unitOfWork = unitOfWork;
_requestUserProvider = requestUserProvider;
}
public IActionResult Update()
{
//this is easy
string userId = _requestUserProvider.GetUserId();
//how do I do the setup in moq for this?
IEnumerable<UserProfileData> userProfileQuestions = _unitOfWork.UserProfileDataRepository.Find(x => x.ApplicationUserId == userId);
if (userProfileQuestions != null)
{
ProfileViewModel profileViewModel = new ProfileViewModel();
profileViewModel.UserProfileData = userProfileQuestions.FirstOrDefault();
return View(profileViewModel);
}
return View("Error", "Home");
}
EDIT 1: MY IUnitOfWork and implementation of the method
public interface IUnitOfWork
{
IRepository<ApplicationUser> ApplicationUserRepository { get; }
IRepository<RefMedicalSpecialty> RefMedicalSpecialtyRepository { get; }
IRepository<RefProgramDetailData> RefProgramDetailDataRepository { get; }
IRepository<RefProgramProfileData> ProgramProfileDataRepository { get; }
IRepository<UserProgram> UserProgramRepository { get; }
IRepository<UserFeedback> UserFeedbackRepository { get; }
IRepository<UserAction> UserActionRepository { get; }
IRepository<UserProfileData> UserProfileDataRepository { get; }
IRepository<RawCredential> RawCredentialRepository { get; }
IRepository<RefProgramCharacteristic> RefProgramCharacteristicRepository { get; }
IRepository<UserProgramRefProgramCharacteristic> UserProgramRefProgramCharacteristicRepository { get; }
void Commit();
void RejectChanges();
void Dispose();
}
public IRepository<UserProfileData> UserProfileDataRepository =>
new Repository<UserProfileData>(_retContext);
Declare the Method Find as virtual
public virtual YourType Find(Expression<Func<YourClass, bool>> yourfunc)
and the mock as:
mockUnitOfWork.Setup(x => x.UserProfileDataRepository.Find(It.IsAny<Expression<Func<YourClass, bool>>>()).Returns(DefineYourReturn);
I have person model class defined as:
public class PersonModel
{
public bool SelectionSubmitted = false;
public bool ShowValidationSummary = false;
public string Name;
public string Get()
{
//actual implementation return some value from the db
return string.Empty;
}
}
The controller implementation is as follows:
class HomeController : Controller
{
[HttpGet]
public ActionResult Index(PersonModel model)
{
if (model.SelectionSubmitted && !ValidateSelections(model))
{
model.ShowValidationSummary = true;
}
return View("Index", model.Get());
}
private bool ValidateSelections(PersonModel model)
{
if(model.Name == "")
{
ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
}
return ModelState.IsValid;
}
}
The test class and method is defined as:
[TestClass]
public class ChildWithoutPlacementControllerTest
{
private readonly Mock<PersonModel> _mockPersonModel;
public ChildWithoutPlacementControllerTest()
{
_mockPersonModel = new Mock<PersonModel>();
}
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown()
{
//Arrange
HomeController controller = new HomeController();
_mockPersonModel.Setup(x => x.Get()).Returns(It.IsAny<string>());
_mockPersonModel.SetupGet(x => x.Name).Returns(string.Empty);
_mockPersonModel.SetupGet(x => x.SelectionSubmitted).Returns(true);
//Act
controller.Index(_mockPersonModel.Object);
//Assert
var isShowSummarySetToTrue = _mockPersonModel.Object.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
}
What I want to achieve is mock the SelectionSubmitted and Name property to true and string.Empty respectively also Setup the Get method of PersonModel class, and check if the test return object has ShowValidationSummary set to true.
However, I am getting that I can't set up the non-virtual property Name.
Am I doing something wrong or is there any way to do it without changing the implementation code?
Am I doing something wrong
This appears to be an XY problem.
is there any way to do it without changing the implementation code
There really is no need for moq in this scenario. You can use inheritance to craft a fake model to be used in the test. The fake model will override the method that is tightly coupled to the database. (more on that later)
public class FakePerson : PersonModel {
public new string Get() {
return string.Empty; //Not calling the base Get
}
}
The test can then be refactored to use the fake model and be exercised to completion as intended.
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
//Arrange
var fakePersonModel = new FakePerson() {
Name = string.Empty,
SelectionSubmitted = true
};
var controller = new HomeController();
//Act
controller.Index(fakePersonModel);
//Assert
var isShowSummarySetToTrue = fakePersonModel.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
That aside, your model appears to be doing to much if the actual Get implementation does as you stated here
actual implementation return some value from the db
Consider refactoring that functionality out into a service (Single Responsibility Principle / Separation of Concerns)
public interface IPersonModelService {
string Get(PersonModel person);
}
public class PersonModelService : IPersonModelService {
public string Get(PersonModel person) {
//actual implementation return some value from the db
}
}
and keep the model as lean as possible. Also consider refactoring those public fields into public properties.
public class PersonModel {
public bool SelectionSubmitted { get; set; }
public bool ShowValidationSummary { get; set; }
public string Name { get; set; }
}
The controller would depend on the service abstraction
class HomeController : Controller {
private IPersonModelService service;
public HomeController(IPersonModelService service) {
this.service = service;
}
[HttpGet]
public ActionResult Index(PersonModel model) {
if (model.SelectionSubmitted && !ValidateSelections(model)) {
model.ShowValidationSummary = true;
}
return View("Index", service.Get(model));
}
private bool ValidateSelections(PersonModel model) {
if (model.Name == "") {
ModelState.AddModelError("EmptyPersonName", "Person name cannot be null");
}
return ModelState.IsValid;
}
}
And now the test can be exercised to completion in isolation.
[TestMethod]
public void GivenPerson_WhenSearchingForFutureBirthDate_ThenValidationMessageShouldBeShown() {
//Arrange
var model = new PersonModel() {
Name = string.Empty,
SelectionSubmitted = true
};
var serviceMock = new Mock<IPersonModelService>();
serviceMock.Setup(_ => _.Get(It.IsAny<PersonModel>())).Returns(string.Empty);
var controller = new HomeController(serviceMock.Object);
//Act
controller.Index(model);
//Assert
var isShowSummarySetToTrue = model.ShowValidationSummary;
Assert.IsTrue(isShowSummarySetToTrue);
}
I extended my User class:
public class ApplicationUser : IdentityUser
{
public bool Blocked { get; set; }
}
and wrote a method:
[Authorize(Roles="admin")]
public ActionResult Ban(string id)
{
var context = new ApplicationDbContext();
var user = context.Users.Find(id);
if(user.Blocked)
{
user.Blocked = false;
}
else
{
user.Blocked = true;
}
context.SaveChanges();
return RedirectToAction("ViewUsers", "Admin");
}
How can I test it using Moq?
Also I've no idea how to test this method:
[Authorize(Roles = "admin")]
public ActionResult ViewUsers()
{
var context = new ApplicationDbContext();
var users = from u in context.Users
where u.Roles.Any(r => r.Role.Name == "user")
select u;
ViewBag.Users = users;
return View(users.ToList());
}
I've tried to mock ApplicationDbContext or UserManager, but I think I don't know something important. Thank you.
Using Asp.net MVC 4 and EF 5.0
THE PROBLEM:
Whenever I choose a user in the dropdownlist and submit the post to the Action.
Two things happen:
The user is added to the DinnerEvent as supposed to:
The User that was selected is duplicated, so that I now have two instances of the user in the User table in the database. Which is bad. :(
Here is the setup
I have created two entities:
DinnerEvent and User
The DinnerEvent class has a navigation property to a collection of Attendants (Users)
public virtual ICollection<User> Attendants { get; set; }
I have created repositories for both DinnerEvent and Users
The AddAttendantToEvent Action in the DinnerEvent Controller
[HttpPost]
public ActionResult AddAttendantToEvent(int users , int EventID )
{
if (ModelState.IsValid)
{
var user = userRepository.Find(users);
dinnereventRepository.Find(EventID).Attendants.Add(user); //Add the User to the Event.
dinnereventRepository.Save();
return RedirectToAction("Index");
}
else
{
return View();
}
}
The view - Iterating over all the events, and adding a dropdownlist populated with all the users foreach event.
#model Madklub.Models.ViewModel
#foreach (var item in Model.events) {
//Code to display available events
#using (Html.BeginForm("AddAttendantToEvent", "DinnerEvents")) {
#Html.Hidden("EventID", item.DinnerEventID);
#Html.DropDownListFor(m => m.users, new SelectList(Model.users, "UserID", "RoomNumber"));
<input type="submit" value="Add" />
}
ViewModel:
public class ViewModel
{
public IQueryable<DinnerEvent> events { get; set; }
public IQueryable<User> users { get; set; }
}
which is initialized like this:
Index Action in DinnerEventController:
public ViewResult Index()
{
ViewModel viewdata = new ViewModel();
viewdata.events = dinnereventRepository.AllIncluding(m => m.Attendants);
viewdata.users = userRepository.All;
return View(viewdata);
}
What am I doing wrong?
The repository code as requested:
Scaled down to only contain the Save() method.
Note that this is all autogenerated code by Scaffolding.
public class DinnerEventRepository : IDinnerEventRepository
{
MadklubContext context = new MadklubContext();
public void Save()
{
context.SaveChanges();
}
}
public class MadklubContext : DbContext
{
public DbSet<Madklub.Models.User> Users { get; set; }
public DbSet<Madklub.Models.MadklubEvent> MadklubEvents { get; set; }
}
Unit of Work pattern: (BTW, make your repo's/uow IDisposable so you can clean up the DbContext instances)
public class DinnerEventRepository : IDinnerEventRepository
{
MadklubContext _context = new MadklubContext();
public void Save()
{
_context.SaveChanges();
}
public DinnerEventRepository( MadklubContext context = null )
{
_context = context ?? new MadklubContext();
}
}
public class UserRepository //: IUserRepository
{
MadklubContext _context = new MadklubContext();
public void Save()
{
_context.SaveChanges();
}
public UserRepository( MadklubContext context = null )
{
_context = context ?? new MadklubContext();
}
}
public class RsvpUnitOfWork // come up with a better name
{
MadklubContext _context = new MadklubContext();
public DinnerEventRepository DinnerEventRepo { get; private set; }
public UserRepository UserRepo { get; private set; }
public RsvpUnitOfWork()
{
DinnerEventRepo = new DinnerEventRepository( _context );
UserRepo = new UserRepository( _context );
}
public void Save()
{
_context.SaveChanges();
}
}
Usage:
[HttpPost]
public ActionResult AddAttendantToEvent(int users , int EventID )
{
if (ModelState.IsValid)
{
// this should be a using statement once you implement IDisposable
// e.g. using( var uow = new RsvpUnitOfWork() ) { ... }
var uow = new RsvpUnitOfWork();
// you need to validate that user != null
var user = uow.UserRepo.Find(users);
// possible null reference exception if dinner event not found for EventID
uow.DinnerEventRepo.Find(EventID).Attendants.Add(user); //Add the User to the Event.
uow.Save();
return RedirectToAction("Index");
}
else
{
return View();
}
}