Problem
I’m new to unit testing and have created a unit test for a create user action method and it’s passing the test. I’m not sure if this is the right way to unit test a create method.
This is my test method:
[TestMethod]
public void Create_User()
{
Mapper.CreateMap<UserViewModel, User>();
//Arrange
var membershipRepository = Mock.Create<IStaticMembershipService>();
var userMock = Mock.Create<MembershipUser>();
Mock.Arrange(() => userMock.ProviderUserKey).Returns(1);
Mock.Arrange(() => membershipRepository.GetUser()).Returns(userMock);
var UserRepository = Mock.Create<IUserRepository>();
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write("Creating Streams for Testing");
writer.Flush();
stream.Position = 0;
var mockFile = Mock.Create<HttpPostedFileBase>();
Mock.Arrange(() => mockFile.ContentType).Returns("application/pdf");
Mock.Arrange(() => mockFile.FileName).Returns("Test.pdf");
Mock.Arrange(() => mockFile.InputStream).Returns(stream);
Mock.Arrange(() => mockFile.ContentLength).Returns(1);
UserViewModel mockUserViewModel = new UserViewModel { Description = "TestDesc", Title = "Tester", UserId = 1, File = mockFile };
Mock.Arrange(() => UserRepository.AddUser(Arg.IsAny<User>())).Returns(new OperationStatus{Status = true});
//Act
UserController controller = new UserController(UserRepository, membershipRepository);
RedirectToRouteResult actionResult = (RedirectToRouteResult)controller.Create(mockUserViewModel);
//Assert
Assert.AreEqual(actionResult.RouteValues["Action"], "Index");
}
This is the action method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(UserViewModel item)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserViewModel, User>(item);
//Populates file name and content type to newUser
UserHelper.PopulateFile(item.File, newUser);
newUser.UserId = (int)_membershipService.GetUser().ProviderUserKey;
var opStatus = _UserRepository.AddUser(newUser);
if (!opStatus.Status)
throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
return RedirectToAction("Index");
}
else
return View(item);
}
Any relevant controller info:
private IUserRepository _UserRepository;
private IStaticMembershipService _membershipService;
public UserController(IUserRepository UserRepo, IStaticMembershipService membershipService)
{
_UserRepository = UserRepo;
_membershipService = membershipService;
}
IUserRepository
:
public interface IUserRepository
{
List<User> GetAllUsers(int Id);
OperationStatus AddUser(User newUser);
User GetUserById(int Id);
}
UserRepository containing definitions of above interfaces:
public List<User> GetAllUsers(int Id)
{
using (DataContext)
{
return DataContext.Users.Where(j => j.Id == Id).ToList();
}
}
public Journal GetUserById(int Id)
{
using (DataContext)
return DataContext.Users.SingleOrDefault(j => j.UserId == Id);
}
public OperationStatus AddUser(Journal newUser)
{
var opStatus = new OperationStatus { Status = true };
try
{
using (DataContext)
{
newUser.ModifiedDate = DateTime.Now;
var j = DataContext.Users.Add(newUser);
DataContext.SaveChanges();
}
}
catch (Exception e)
{
//opStatus = OperationStatus.CreateFromException("Error adding user: ", e);
}
return opStatus;
}
The test is running fine. I’m just not sure if it’s the right way for unit testing. Please let me know if I’m following the right approach here.
Solution
I think you are not testing everything. The API should create a user but the only thing you assert is the ActionResult
Assert.AreEqual(actionResult.RouteValues["Action"], "Index");
So the method runs and the result is correct. But has it actually created a new user and add it to the repository? How about this operation?
var opStatus = _UserRepository.AddUser(newUser);
IMO you should also check the repository to see whether the API correctly added the new user to it. I don’t know what interfaces the UserRepository
might implement but for this
var UserRepository = Mock.Create<IUserRepository>();
assertions like these ones could be appropriate (just examples) or anything else that makes sense for the repository.
Assert.AreEqual(1, UserRepository.Count);
Assert.AreEqual(referenceUser, UserRepository.GetUser(abc));
What if someone had a problem with the repository and commented it out and actually forgot to add #if DEBUG
or made any other changes ignoring the status-code?
public ActionResult Create(UserViewModel item)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserViewModel, User>(item);
//Populates file name and content type to newUser
UserHelper.PopulateFile(item.File, newUser);
newUser.UserId = (int)_membershipService.GetUser().ProviderUserKey;
// I temporarily disabled the repository because I didn't work for me.
//var opStatus = _UserRepository.AddUser(newUser);
//if (!opStatus.Status)
// throw new System.Web.Http.HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
return RedirectToAction("Index");
}
else
return View(item);
}
The test would still pass but the result would be wrong.