I'm trying to write test cases for my web api methods.
[Fact]
public async Task Get_Message_By_Id()
{
var messages = new Messages()
{
MessageId = 1,
Recipient = "1;2;3",
Subject = "Test Subject",
MessageText = "test subject"
};
var mesBL = new Mock<IMessagesBusinessLogic>();
mesBL
.Setup(repo => repo.GetRecivedMessageById(It.IsAny<IWebToken>() ,messages.MessageId))
.Returns(Task.FromResult(messages));
var messageController = new MessagesController(mesBL.Object);
var contentResult = await messageController.GetRecivedMessageById(messages.MessageId) as OkObjectResult;
Assert.NotNull(contentResult);
}
I get an error of null reference while GetRecivedMessageById method call.
Controller method;
[HttpGet]
[Route("{id:int}")]
public async Task<IActionResult> GetRecivedMessageById(int id)
{
return Ok(await _messagesBusinessLogic.GetRecivedMessageById(User.GetWebToken(), id));
}
Here, issue is because, user identity passing NULL.
How can we pass it from Test?
User is null because it was not setup in the test before exercising the method under test.
//Create test user
var displayName = "User name";
var role = "SomeRole";
var token = new Mock<IWebToken>();
token.Setup(m => m.DisplayName).Returns(displayName);
token.Setup(m => m.Role).Returns(role);
var identity = new UserIdentity(token.Object);
var principal = new GenericPrincipal(identity, roles: new string[] { });
var user = new ClaimsPrincipal(principal);
// Set the User on the controller via the ControllerContext
var messageController = new MessagesController(mesBL.Object) {
ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
Related
Here is my test with a new Mock and its setup:
[Fact]
public async Task RegisterUser_MustReturnPhoneNumberError2()
{
var userRepository = new Mock<IUserRepository>()
.Setup(_ => _.GetUserByUsernameAndPassword(It.IsAny<string>(), It.IsAny<string>()))
.Returns(new User
{
Password = "b",
EmailAddress = "asdadsa!yaho",
Name = "asa"
});
var userService = new UserService(new Mock<IUserRepository>().Object);
var expectation = new UserResponseDto
{
Status = new StatusMaker().ErrorStatus("TEXT")
};
var a = new UserCredentialRequestDto
{
EmailAddress = "a",
Password = "b",
PhoneNumber = "c"
};
var res = await userService.LoginUser(a);
Assert.Equal(expectation.Status.Message, res.Status.Message);
}
and here is the real method:
public async Task<UserResponseDto> LoginUser(UserCredentialRequestDto userCredential)
{
var user = _userRepository.GetUserByUsernameAndPassword(userCredential.PhoneNumber,
userCredential.Password.DoHash());
// If user is Null
#region Error Status
if (user is null)
{
return new UserResponseDto
{
Status = new StatusMaker().ErrorStatus("ABCD")
};
}else{
return new UserResponseDto
{
Status = new StatusMaker().ErrorStatus("TEXT")
};
}
After I run debug the test, the GetUserByUsernameAndPassword() function would be null again. Why the return of GetUserByUsernameAndPassword() would be null even after Mock Setup?
You're passing a newly instantiated Mock of IUserRepository.
Try the following
var userRepository = new Mock<IUserRepository>()
.Setup(_ => _.GetUserByUsernameAndPassword(It.IsAny<string>(), It.IsAny<string>()))
.Returns(new User
{
Password = "b",
EmailAddress = "asdadsa!yaho",
Name = "asa"
});
var userService = new UserService(userRepository.Object);
This is my function of a controller class, I want to test this but I didn't have any idea to know why checking the OkObject is null. I need some advice and a way to solve this:
[HttpGet]
public async Task<IActionResult> LoginAsync(string phoneNumber, string pass)
{
User login = new User();
login.Phone = phoneNumber;
login.Password = pass;
IActionResult response = Unauthorized();
var user = await _loginService.AuthenticateUserAsync(login);
if(user != null)
{
var tokenStr = _loginService.GenerateJWT(user);
response = Ok(new { token = tokenStr });
}
return response;
}
My test function is :
[Fact]
public async Task LoginAsync_ReturnOk()
{
var mock = new Mock<ILoginService>();
var controller = new LoginController(mock.Object);
var phone = "0123456789";
var pass = "abc123";
var okResult = await controller.LoginAsync(phone, pass);
Assert.IsType<OkObjectResult>(okResult as OkObjectResult);
}
I really need help from you guys.
The test is failing because the mocked dependency has not been configured to behave as expected for the current test case.
The subject under test for the stated test case needs
//...
if(user != null)
//...
to be true but the _loginService was not configured for the test case
//...
var user = await _loginService.AuthenticateUserAsync(login);
//...
That means that the subject under test will return UnauthorizedResult
This will cause okResult as OkObjectResult to be null
The test needs to be arranged so that the test case, when exercised, will behave as expected
[Fact]
public async Task LoginAsync_Should_Return_Ok() {
// Arrange
var mock = new Mock<ILoginService>();
var user = new UserResult(); //Or what ever the actual user type is
mock.Setup(_ => _.AuthenticateUserAsync(It.IsAny<User>()))
.ReturnsAsync(user);
string tokenStr = "some token value";
mock.Setup(_ => _.GenerateJWT(user)).Returns(tokenStr);
LoginController controller = new LoginController(mock.Object);
string phone = "0123456789";
string pass = "abc123";
//Act
IActionResult response = await controller.LoginAsync(phone, pass);
//Assert
OkObjectResult okResult = Assert.IsType<OkObjectResult>(response);
//Optional: additional assertions for this test case
dynamic value = okResult.Value;
Assert.NotNull(value);
string token = (string)value.token;
Assert.Equal(token, tokenStr);
}
And since there is also the possible outcome of having an unauthorized response, here is the other test case to cover that controller action
[Fact]
public async Task LoginAsync_Should_Return_Unauthorized() {
//Arrange
var mock = new Mock<ILoginService>();
mock.Setup(_ => _.AuthenticateUserAsync(It.IsAny<User>()))
.ReturnsAsync((UserResult)null); //Or what ever the actual user type is
LoginController controller = new LoginController(mock.Object);
string phone = "0123456789";
string pass = "abc123";
//Act
IActionResult response = await controller.LoginAsync(phone, pass);
//Assert
Assert.IsType<UnauthorizedResult>(response);
}
I'm having a problem working out how and what to test.
I have a controller that injects UserManager and calls the CreateAsync method to create a new user.
I don't want to test the Identity user manager as this has clearly been thoroughly tested already. What I would like to do is test that the controller runs through the correct paths (in my case, there are 3 paths, sending responses back with either model state errors, identity response errors or a simple string)
Should I be trying to create a mock of the user manager in order to create my test (I'm not sure how to set up user manager as a mock dependency)
Second, how can I set conditions to verify that the controller has taken a given path.
I am using xUnit and Moq.
[Route("api/[controller]")]
public class MembershipController : BaseApiController
{
private UserManager<ApplicationUser> _userManager;
public MembershipController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[HttpGet("RegisterNewUser")]
public HttpResponseMessage RegisterNewUser([FromBody] NewUserRegistration user)
{
if (ModelState.IsValid)
{
ApplicationUser newUser = new ApplicationUser();
newUser.UserName = user.username;
newUser.Email = user.password;
IdentityResult result = _userManager.CreateAsync(newUser, user.password).Result;
if (result.Errors.Count() > 0)
{
var errors = new IdentityResultErrorResponse().returnResponseErrors(result.Errors);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
}
else
{
var errors = new ViewModelResultErrorResponse().returnResponseErrors(ModelState);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
return this.WebApiResponse(
"We have sent a valifation email to you, please click on the verify email account link.",
HttpStatusCode.OK);
}
}
In My unit test I have the following to test a happy path scenario
[Fact]
public void RegisterNewUser_ReturnsHttpStatusOK_WhenValidModelPosted()
{
var mockStore = new Mock<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore.Object, null, null, null, null, null, null, null, null);
ApplicationUser testUser = new ApplicationUser { UserName = "user#test.com" };
mockStore.Setup(x => x.CreateAsync(testUser, It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(IdentityResult.Success));
mockStore.Setup(x => x.FindByNameAsync(testUser.UserName, It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(testUser));
mockUserManager.Setup(x => x.CreateAsync(testUser).Result).Returns(new IdentityResult());
MembershipController sut = new MembershipController(mockUserManager.Object);
var input = new NewUserInputBuilder().Build();
sut.RegisterNewUser(input);
}
Where "input" in sut.RegisterNewUser(input); refers to a helper class which constructs the viewmodel which the controller action requires:
public class NewUserInputBuilder
{
private string username { get; set; }
private string password { get; set; }
private string passwordConfirmation { get; set; }
private string firstname { get; set; }
private string lastname { get; set; }
internal NewUserInputBuilder()
{
this.username = "user#test.com";
this.password = "password";
this.passwordConfirmation = "password";
this.firstname = "user";
this.lastname = "name";
}
internal NewUserInputBuilder WithNoUsername()
{
this.username = "";
return this;
}
internal NewUserInputBuilder WithMisMatchedPasswordConfirmation()
{
this.passwordConfirmation = "MismatchedPassword";
return this;
}
internal NewUserRegistration Build()
{
return new NewUserRegistration
{ username = this.username, password = this.password,
passwordConfirmation = this.passwordConfirmation,
firstname = this.firstname, lastname = this.lastname
};
}
}
My aim here is to force 3 conditions via tests:
Create a valid viewmodel and return a success message
Create a valid viewmodel but returns a IdentityResponse error (eg. user exists) which gets converted to
Create an invalid viewmodel and returns Modelstate errors
The errors are handled using a abstract class which returns a json object
The base class for the controller simply constructs a HttpResponseMessage for return.
Basically I want to check that the correct error response class is called by forcing the test down the modelstate error path, the identityresult.errors path and that the happy path can be achieved.
Then my plan is to test the error response classes in isolation.
Hopefully that is enough detail.
Mehod under test should be made async and not use blocking calls ie .Result
[HttpGet("RegisterNewUser")]
public async Task<HttpResponseMessage> RegisterNewUser([FromBody] NewUserRegistration user) {
if (ModelState.IsValid) {
var newUser = new ApplicationUser() {
UserName = user.username,
Email = user.password
};
var result = await _userManager.CreateAsync(newUser, user.password);
if (result.Errors.Count() > 0) {
var errors = new IdentityResultErrorResponse().returnResponseErrors(result.Errors);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
} else {
var errors = new ViewModelResultErrorResponse().returnResponseErrors(ModelState);
return this.WebApiResponse(errors, HttpStatusCode.BadRequest);
}
return this.WebApiResponse(
"We have sent a valifation email to you, please click on the verify email account link.",
HttpStatusCode.OK);
}
Review of Happy path scenario and method under test shows that there is no need to setup the UserStore as test will be overriding the user manager virtual members directly.
Note the test has also been made async as well.
Create a valid viewmodel and return a success message
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusOK_WhenValidModelPosted() {
//Arrange
var mockStore = Mock.Of<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore, null, null, null, null, null, null, null, null);
mockUserManager
.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
.ReturnsAsync(IdentityResult.Success);
var sut = new MembershipController(mockUserManager.Object);
var input = new NewUserInputBuilder().Build();
//Act
var actual = await sut.RegisterNewUser(input);
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.IsSuccessStatusCode == true);
}
Create a valid viewmodel but returns a IdentityResponse error (eg. user exists) which gets converted
For this you just need to setup the mock to return a result with errors.
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusBadRequest_WhenViewModelPosted() {
//Arrange
//...code removed for brevity
mockUserManager
.Setup(x => x.CreateAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>()))
.ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "test"}));
//...code removed for brevity
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.StatusCode == HttpStatusCode.BadRequest);
}
And for
Create an invalid viewmodel and returns Modelstate errors
You just need to set the model state of the controller so that it is invalid.
[Fact]
public async Task RegisterNewUser_ReturnsHttpStatusBadRequest_WhenInvalidModelState() {
//Arrange
var mockStore = Mock.Of<IUserStore<ApplicationUser>>();
var mockUserManager = new Mock<UserManager<ApplicationUser>>(mockStore, null, null, null, null, null, null, null, null);
var sut = new MembershipController(mockUserManager.Object);
sut.ModelState.AddModelError("", "invalid data");
var input = new NewUserInputBuilder().Build();
//Act
var actual = await sut.RegisterNewUser(input);
//Assert
actual
.Should().NotBeNull()
.And.Match<HttpResponseMessage>(_ => _.StatusCode == HttpStatusCode.BadRequest);
}
FluentAssertions were used to do all the assertions. You could just as easily used Assert.* API.
This should be enough to get you on your way with the above question.
Here's a simple way using NUnit (you could do something similar with xUnit), if you don't want to test the user manager. (I've also shown how the DbContext can be passed to the same controller, using an in-memory database that can be used for setting up mock data)
private DbContextOptions<MyContextName> options;
[OneTimeSetUp]
public void SetUp()
{
options = new DbContextOptionsBuilder<MyContextName>()
.UseInMemoryDatabase(databaseName: "MyDatabase")
.Options;
// Insert seed data into the in-memory mock database using one instance of the context
using (var context = new MyContextName(options))
{
var testWibble = new Wibble { MyProperty = 1, MyOtherProperty = 2 ... };
context.wibbles.Add(testWibble);
context.SaveChanges();
}
}
[Test]
public void Some_TestMethod()
{
// Use a clean instance of the context to run the test
using (var context = new MyDbContext(options))
{
var store = new UserStore<MyUserType>(context);
var userManager = new UserManager<MyUserType>(store, null, null, null, null, null, null, null, null);
MyController MyController = new MyController(userManager, context);
... test the controller
}
}
inside unit test I'm faking http context using
var fakeHttpContext = new Mock<HttpContextBase>();
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(t => t.HttpContext).Returns(fakeHttpContext.Object);
this.controller.ControllerContext = controllerContext.Object;
now inside my code I'm using following
UrlHelper helper = new UrlHelper(this.ControllerContext.RequestContext);
string url = helper.Action("Details", "Pers", new { id = person.Id });
what should I mock inside unit test in order to use UrlHelper code?
Currently UrlHelper helper is null.
p.s. Because of clarity I did not show whole initialization of controller code inside test, it's working, but I'm struggling now with this request context and UrlHelper.
Controller already has a UrlHelper Url property that you can pass an instance or mock. no need to new one up in the code.
Take a look at this example test with a controller that uses the UrlHelper.
[TestClass]
public class UrlHelperTest {
[TestMethod]
public void MockUrlHelper() {
//Arrange
var requestUrl = new Uri("http://myrequesturl");
var request = Mock.Of<HttpRequestBase>();
var requestMock = Mock.Get(request);
requestMock.Setup(m => m.Url).Returns(requestUrl);
var httpcontext = Mock.Of<HttpContextBase>();
var httpcontextSetup = Mock.Get(httpcontext);
httpcontextSetup.Setup(m => m.Request).Returns(request);
var actionName = "MyTargetActionName";
var expectedUrl = "http://myfakeactionurl.com";
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper
.Setup(m => m.Action(actionName, "Register", It.IsAny<object>(), It.IsAny<string>()))
.Returns(expectedUrl)
.Verifiable();
var sut = new MyController();
sut.Url = mockUrlHelper.Object;
sut.ControllerContext = new ControllerContext {
Controller = sut,
HttpContext = httpcontext,
};
//Act
var result = sut.MyAction();
//Assert
mockUrlHelper.Verify();
}
public class MyController : Controller {
[HttpPost]
public ActionResult MyAction() {
var link = GenerateActionLink("MyTargetActionName", string.Empty, string.Empty);
return View((object)link);
}
private string GenerateActionLink(string actionName, string token, string username) {
string validationLink = null;
if (Request.Url != null) {
var encodedToken = EncodedUrlParameter(token);
var url = Url.Action(actionName, "Register", new { Token = encodedToken, Username = username }, Request.Url.Scheme);
validationLink = url;
}
return validationLink;
}
private string EncodedUrlParameter(string token) {
return "Fake encoding";
}
}
}
I am doing unit tests using NUnit and Moq. I tried this example that checks whether a session object called Role exists. If not, it creates it, and returns an object of type Role.
protected Role GetRole()
{
if (Session["Role"] == null)
{
Session["Role"] = new Role();
}
return Session["Role"] as Role;
}
Then I use it in the Index action:
public ActionResult Index()
{
var roles = GetRole();
roles.RoleName = "Test";
return View();
}
And this is my test:
[Test]
public void TestMethod1()
{
var contextMock = new Mock<ControllerContext>();
var mockHttpContext = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
mockHttpContext.Setup(ctx => ctx.Session).Returns(session.Object);
contextMock.Setup(ctx => ctx.HttpContext).Returns(mockHttpContext.Object);
contextMock.Setup(p => p.HttpContext.Session["Role"]).Returns(new Role
{
RoleId = 1,
RoleName = "Test"
});
var homeController = new HomeController();
homeController.ControllerContext = contextMock.Object;
var indexView = homeController.Index();
Assert.IsNotNull(indexView);
}
It runs successfully. But when I check the code coverage, it gives me that the Session["Role"] = new Role(); part is not covered by the test code. So I made another test. There I don't setup the session variable Role:
[Test]
public void TestMethod2()
{
var contextMock = new Mock<ControllerContext>();
var mockHttpContext = new Mock<HttpContextBase>();
var session = new Mock<HttpSessionStateBase>();
mockHttpContext.Setup(ctx => ctx.Session).Returns(session.Object);
contextMock.Setup(ctx => ctx.HttpContext).Returns(mockHttpContext.Object);
var homeController = new HomeController();
homeController.ControllerContext = contextMock.Object;
var indexView = homeController.Index();
Assert.IsNotNull(indexView);
Assert.IsNull(homeController.ControllerContext.HttpContext.Session["Role"]);
}
But it fails - it gives System.NullReferenceException : Object reference not set to an instance of an object because of the roles.RoleName = "Test"; row. How to make it run?
Thank you in advance!
The problem you have is that in the second test Session["Role"] will never return anything as it's a mocked object set up to always return null. One possible workaround is to change your GetRole function to this and adjust your tests:
protected Role GetRole()
{
var role = Session["Role"] as Role;
if (role == null)
{
role = new Role();
Session["Role"] = role;
}
return role;
}