I have a business class that manage a USER entity.
In this class I have a method to return a single user by id:
public Utente GetUser(int id)
{
var utente = _userDataManager.GetUserById(id);
return _mapper.Map<Utente>(utente);
}
_userDataManager is an interface, IUSERDATAMANAGER, and it has implemented with a DAL class; GetUserById return a user or null (search made with EF6).
_mapper is a IMAPPER interface (automapper).
The method return is the mapped object.
I have two question:
Does it make sense to test this method?
Should I mock both the object?
A black-boxed example will be appreciated.
Does it make sense to test this method?
If it is worth writing the code it is worth testing the code.
Should I mock both the object?
When testing a subject under test, you mock the dependencies that would allow the test to be exercised to completion.
For example
public void GetUser_Should_Return_Utente() {
//Arrange
var userId = 2;
var user = new User {
UserId = userId,
//... other properties
};
var userDataManagerMock = new Mock<IUserDataManager>();
userDataManagerMock.Setup(_ => _.GetUserById(userId)).Returns(user);
var expected = new Utente {
Id = user.Id,
//...other properties
}
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(_ => _.Map<Utente>(It.IsAny<object>())).Returns(expected);
var subject = new MyBusinessClass(userDataManagerMock.Object, mapperMock.Object);
//Act
var actual = subject.GetUser(userId);
//Assert
Assert.Equal(expected, actual);
}
In the above code the user data manager and the mapper a mocked and injected into the subject when testing the GetUser method.
This is an isolated unit test of the above method and shows the the current implementation of that method will flow to completion provided the dependencies perform as expected during invocation.
Related
I wrote a few tests in XUnit to test my data access layer. I instantiated my DAL objects & configs the same way I would if I were using it in the actual web application(this is configured to run against a dev environment for testing purposes), however XUnit throws an error:
Message: The following constructor parameters did not have matching fixture data: IConfiguration config, IMediaDataAccess media
I'm a bit new to XUnit, so unsure what the problem is. Normally ASP.NET would inject instances of IConfiguration and IMediaDataAccess for me, but that doesn't seem to be the case here.
My Test class & a sample test case:
public class DataAccessLayerTests
{
public IConfiguration Config { get; set; }
private IMediaDataAccess mediaData;
public IMediaDataAccess MediaData { get => mediaData; set => mediaData = value; }
public DataAccessLayerTests(IConfiguration config, IMediaDataAccess media)
{
this.MediaData = media;
this.Config = config;
}
public void GetAllMediaAsync_MediaListIsReturned()
{
List<Media> testData = mediaData.GetAllMedia().Result;
Assert.IsType<List<Media>>(testData);
}
}
The test(s) all fail due to the following error: Message: The following constructor parameters did not have matching fixture data: IConfiguration config, IMediaDataAccess media
For anyone else having this problem, Alexey's comment is correct. You need to download a mocking framework (like Moq) and use it to mock up the dependencies your code is expecting. For example, below is one of my fixed unit tests:
public void IndexDataModel_ShouldDisplayMedia()
{
var mockLogger = new Mock<ILogger<IndexModel>>();
var mockDataAccess = new Mock<IMediaDataAccess>();
mockDataAccess.Setup(media => media.GetAllMedia()).ReturnsAsync(GetTestMedia());
IndexModel indexController = new IndexModel(mockDataAccess.Object, mockLogger.Object);
var result = indexController.OnGet();
var viewResult = Assert.IsType<PageResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<Media>>(
indexController.mediaList);
}
The trick is you need to mock up anything that you normally depend on getting injected into your constructor, in my case this was:
var mockLogger = new Mock<ILogger<IndexModel>>();
var mockDataAccess = new Mock<IMediaDataAccess>();
My constructor takes both an ILogger and an IMediaDataAccess, so I needed to mock those. Additionally, the other code is there to provide dummy return values when your mocked dependencies are used by the test. This is done with the .Setup line of code. All this does(I think) is when GetAllMedia() is called, the mock object returns the contents of GetTestMedia() instead of needing to make the actual calls. Just make sure whatever function you write has the same return type as the real function. For reference, this is my GetTestMedia() function:
private List<Media> GetTestMedia()
{
var listMedia = new List<Media>();
Media testMedia = new Media
{
Description = "TestDesc",
Genre = "TestGenre",
Name = "TestName",
Rating = MediaRating.Excellent,
Type = MediaType.Movie,
Id = 1
};
listMedia.Add(testMedia);
Media testMedia2 = new Media
{
Description = "TestDesc2",
Genre = "TestGenre2",
Name = "TestName2",
Rating = MediaRating.Poor,
Type = MediaType.Music,
Id = 2
};
listMedia.Add(testMedia2);
return listMedia;
}
I'm using xUnit & Moq and what I'm trying to achieve is typically create Identity User so it's stored in Entity Framework In-Memory DB.
I have established Seed Data functionality - it gets triggered when my ASP.NET Core host app starts and works flawlessly - so no problem.
But the issue occurs when I use a mocked UserManager. No exceptions are thrown, users are just not being saved.
Verified while debugging tests, DbContext returns 0 users, also UserManager.FindByNameAsync yields null.
I wonder what is the cause. Could it be due to the way I assemble UserManager in constructor of SeedDataTest class?
public class SeedDataTest
{
private AppDbContext dbContext;
private UserManager<ApplicationUser> userManager;
private ITenantService tenantService;
public SeedDataTest()
{
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "in_memory_db")
.Options;
dbContext = new AppDbContext(options);
var userStore = new UserStore<ApplicationUser>(dbContext);
userManager = new Mock<UserManager<ApplicationUser>>(
userStore,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<ApplicationUser>>().Object,
new IUserValidator<ApplicationUser>[0],
new IPasswordValidator<ApplicationUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
.Object;
tenantService = new Mock<TenantService>(dbContext).Object;
}
[Fact]
public void Test1()
{
new TenantsCreator(dbContext).Create();
new UserCreator(dbContext, tenantService, userManager).Create(); // stuck here
new MembershipCreator(dbContext, userManager).Create();
// unfinished
}
}
And here is the code from UserCreator
public class UserCreator
{
private AppDbContext _context;
private ITenantService _tenantService;
private UserManager<ApplicationUser> _userManager;
public UserCreator(
AppDbContext context,
ITenantService tenantService,
UserManager<ApplicationUser> userManager
)
{
_context = context;
_tenantService = tenantService;
_userManager = userManager;
}
public void Create()
{
Task.Run(async () => await CreateUsers()).ConfigureAwait(false).GetAwaiter().GetResult();
}
private async Task CreateUsers()
{
ApplicationUser hostAdminUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.AdminJoe.UserName));
if (hostAdminUser == null)
{
hostAdminUser = new ApplicationUser()
{
FirstName = SetupConsts.Users.AdminJoe.FirstName,
LastName = SetupConsts.Users.AdminJoe.LastName,
UserName = SetupConsts.Users.AdminJoe.UserName,
Email = SetupConsts.Users.AdminJoe.Email,
EmailConfirmed = true,
PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(hostAdminUser, SetupConsts.Users.Passwords.Default)
};
await _userManager.CreateAsync(hostAdminUser);
}
ApplicationUser secondaryUser = _context.Users.FirstOrDefault(x => x.UserName.Equals(SetupConsts.Users.JohnRoe.UserName));
if (secondaryUser == null)
{
secondaryUser = new ApplicationUser()
{
FirstName = SetupConsts.Users.JohnRoe.FirstName,
LastName = SetupConsts.Users.JohnRoe.LastName,
UserName = SetupConsts.Users.JohnRoe.UserName,
Email = SetupConsts.Users.JohnRoe.Email,
EmailConfirmed = true,
PasswordHash = new PasswordHasher<ApplicationUser>().HashPassword(secondaryUser, SetupConsts.Users.Passwords.Default)
};
await _userManager.CreateAsync(secondaryUser);
}
}
}
we use mocking frameworks to allow us to build mocked (substituted) dependencies as well as specify what is returned from classes/interfces used within our SUT.
A good use case for this is a database itself. We always want to know the exact state of objects whilst we are testing them and the only way we do that is by expressing the content of them ourselves.
That is where Moq comes in. One of its features is to allow us to state the result of method calls.
I believe you are seeing 0 results returned because you are using a moq implementation of a class (which doesnt actually call the implemented class). In order to get a result, you will need to do some setup:
mockedClass.Setup(x => x.GetUserDetails(It.IsAny<int>())).Returns(new UserDetails());
Either do it this way, or ensure you are passing a concrete implementation of the UserManager class rather than the mocked version:
userManager = new UserManager<ApplicationUser>
Hope that helps
I am developing an ASP.NET web API application. I am unit testing to every component in my application. I am using Moq Unit Test framework to mock the data. Now I am trying to mock the Configuration.Formatters.JsonFormatter in my Unit test because my action under unit test is using it as follows:
public HttpResponseMessage Register(model)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
Content = new ObjectContent<List<string>>(errors, Configuration.Formatters.JsonFormatter)
};
}
I am trying to mock the Configuration.Formatters.JsonFormatter in the Unit Test as follow.
[TestMethod]
public void Register_ReturnErrorsWithBadRequest_IfValidationFails()
{
PostUserRegistration model = new PostUserRegistration {
Name = "Wai Yan Hein",
Email = "waiyanhein#gmail.com",
Password = ""
};
Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>();
Mock<MediaTypeFormatterCollection> formatterCollection = new Mock<MediaTypeFormatterCollection>();
formatterCollection.Setup(x => x.JsonFormatter).Returns(formatterMock.Object);
Mock<HttpConfiguration> httpConfigMock = new Mock<HttpConfiguration>();
httpConfigMock.Setup(x => x.Formatters).Returns(formatterCollection.Object);
Mock<IAccountRepo> accRepoMock = new Mock<IAccountRepo>();
AccountsController controller = new AccountsController(accRepoMock.Object);
controller.Configuration = httpConfigMock.Object;
controller.ModelState.AddModelError("", "Faking some model error");
HttpResponseMessage response = controller.Register(model);
Assert.AreEqual(response.StatusCode, System.Net.HttpStatusCode.BadRequest);
}
When I run my unit tests, it is giving me this error.
System.NotSupportedException: Invalid setup on a non-virtual
(overridable in VB) member: x => x.JsonFormatter
So, how can I fix that error and how can I mock Configuration.Formatters.JsonFormatter?
You should not have to test that the framework is doing what it was designed to do. Instead, similar to what was suggested before in comments, I suggest using the built in methods of the ApiController and also refactoring the action to the syntax suggested from documentation for that version of Asp.Net Web API
public IHttpActionResult Register(PostUserRegistration model) {
if (!ModelState.IsValid)
return BadRequest(ModelState); //<-- will extract errors from model state
//...code removed for brevity
return Ok();
}
A simple example of a unit test for the above method could look something like this...
[TestMethod]
public void Register_ReturnErrorsWithBadRequest_IfValidationFails() {
//Arrange
PostUserRegistration model = new PostUserRegistration {
Name = "Wai Yan Hein",
Email = "waiyanhein#gmail.com",
Password = ""
};
var accRepoMock = new Mock<IAccountRepo>();
var controller = new AccountsController(accRepoMock.Object);
controller.ModelState.AddModelError("", "Faking some model error");
//Act
var response = controller.Register(model) as InvalidModelStateResult; //<-- Note the cast here
//Assert
Assert.IsNotNull(response);
}
If the injected dependency is not going to be referenced/invoked in the method under test you could also forego the mock and inject null. That however is dependent on code not provided in the original question.
In Asp.net MVC 4.5 , using Microsoft.VisualStudio.TestTools.UnitTesting.
is there a way to really unit test an ActionResult? All documentation I have seen only tests the view name!
Assert.AreEqual("Action Method", result.ViewName);
Well, I want to have a really test. How can I test the response of the controller-action ?
Given something basic along the lines of:
public ActionResult Display(string productCode)
{
var model = new ProductModel(productCode);
if (model.NotFound)
{
return this.RedirectToRoute("NotFound");
}
return this.View("Product", model);
}
Instead of something that asserts like Assert.AreEqual("Action Method", result.ViewName); (which can be a valid test.
You have many options including...
Looking at the model type
[TestMethod]
public void Display_WhenPassedValidProductCode_CreatesModel()
{
using (var controller = this.CreateController())
{
// Arrange Mocks on controller, e.g. a Service or Repository
// Act
var result = controller.Display(string.Empty) as ViewResult;
var model = (ProductModel)result.Model;
Assert.IsInstanceOfType(model, typeof(ProductModel));
}
}
Looking at the model population process
[TestMethod]
public void Display_WhenPassedValidProductCode_PopulatesModel()
{
using (var controller = this.CreateController())
{
const string ProductCode = "123465";
// Arrange Mocks on controller, e.g. a Service or Repository
// Act
var result = controller.Display(ProductCode) as ViewResult;
var model = (ProductModel)result.Model;
Assert.AreEqual(ProductCode, model.ProductCode);
}
}
Looking at the type of action result
[TestMethod]
public void Display_WhenNotFound_Redirects()
{
using (var controller = this.CreateController())
{
const string ProductCode = "789000";
// Arrange Mocks on controller, e.g. a Service or Repository
// Act
var result = controller.Display(ProductCode) as RedirectToRouteResult;
Assert.IsNotNull(result); // An "as" cast will be null if the type does not match
}
}
Basically you can test pretty much anything, pick an example on your code base and try and test it. If you get stuck construct a decent question and post it here.
I'm trying to learn about Unit Testing with the new VS2013 Default MVC w/ Authentication Project. One of the first things I want to test is registering a user. (I know I probably don't need to unit test this since it's already MS tested code but I want to use this to understand the basics). I've also heard that the new Membership code is more 'testable' so I don't need to create my own membership interfaces, etc...
I'm using NSubstitute as a faking framework.
Looking at the Account Controller -> Register() async method
namespace WebApplication1.Controllers
{
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
...
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
If I wanted to write a simple test (ex. Register_RegisterValidUser), how would I do this? I know I need to substitute for the UserManager somehow but this did not work for me:
var substitute = Substitute.For<UserManager<ApplicationUser>>();
I also understand that I need to bypass the async Task<> functions using Task.FromResult but I'm not sure how to return a valid objects from the CreateAsync() and the SigninAsync() methods.
Can somebody help with some sample test code? Many thanks!
To mock the user manager with NSubstitute you have to use this:
var userStore = Substitute.For<IUserStore<ApplicationUser>>();
var userManager = Substitute.For<UserManager<ApplicationUser>>(userStore);
Now you can fake the methods result too. For example:
userManager.FindByNameAsync(Arg.Any<string>()).Returns(Task.FromResult(new ApplicationUser()));
Hope this help you.
Test Guidance
You can Unit Test couple of behaviours, but I will just show you the direction for a single Unit test that just verify whether receive the correct RedirectToAction value. For example,
return RedirectToAction("Index", "Home");
Improving the Testability
In your question you mentioned
I've also heard that the new Membership code is more 'testable' so I
don't need to create my own membership interfaces, etc..
While this is true, I would make a small adjustment so we can make your implementation bit more testable. This way your Unit Test can purely concentrate on the specific behaviour your going test. In other words we can make you SUT (System Under Test) more testable.
await SignInAsync(user, isPersistent: false);
I believe SignInAsync is a private method. There should be some behavior in this method you can probably extract out to a seperate implementation which you can inject in to the SUT. We can call this ISignInManager
public interface ISignInManager {
Task SignInAsync(ApplicationUser user, bool isPersistent);
}
The benefit of this is that now you can inject the behaiovr of an ISignInManager to perform the SignIn tasks and your SUT become more testable. You should see that there will be less mocking/stubbing in your Unit test and make your test easier to write and understand.
Unit Test
You can take the advantage of new async/await usage of the MSTest method. This simplifies complicated and unreliable tests which we used to write.
A Unit Test that verify the correct redirect route controller/action methods, can be written as below.
[TestMethod]
public async Task Register_RegisterValidUser_EnsureRedirectToIndexActionHomeController()
{
// Arrange
var userManagerStub = Substitute.For<IUserManager<ApplicationUser>>();
var tcs = new TaskCompletionSource<IdentityResult>();
tcs.SetResult(new IdentityResult(true));
userManagerStub.CreateAsync(Arg.Any<ApplicationUser>(), Arg.Any<string>()).Returns(tcs.Task);
var signInManagerStub = Substitute.For<ISignInManager>>();
signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));
var sut = new AccountController(userManagerStub) { SignInManager = signInManagerStub.Object };
// Act
var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;
// Assert
Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
}
The above uses NSubstitute as the isolation framework, but if anyone interested in the Moq version, please see below.
[TestMethod]
public async Task Register_RegisterValidUser_EnsureRedirectToIndexHome()
{
// Arrange
var userManagerStub = new Mock<IUserManager<ApplicationUser>>();
var tcs = new TaskCompletionSource<IdentityResult>();
tcs.SetResult(new IdentityResult(true));
userManagerStub.Setup(s => s.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>())).Returns(tcs.Task);
var signInManagerStub = new Mock<ISignInManager>();
signInManagerStub.Setup(s => s.SignInAsync(It.IsAny<ApplicationUser>(), It.IsAny<bool>())).Returns(Task.FromResult(true));
var sut = new AccountController(userManagerStub.Object) {SignInManager = signInManagerStub.Object};
// Act
var result = await sut.Register(new RegisterViewModel() { Password = "fakePw" }) as RedirectToRouteResult;
// Assert
Assert.AreEqual<string>("Index", result.RouteValues["action"].ToString());
Assert.AreEqual<string>("Home", result.RouteValues["controller"].ToString());
}