I have an ASP.NET MVC 4 project with a controller that calls an external WCF to authenticate user login on the VerifyAccount
method. This external WCF returns an AuthModelUserVerification
class back to the controller and creates a Session
containing a user id:
[HttpPost]
public ActionResult VerifyAccount(string username, string password) {
AuthModelUserVerification result = lms_client.VerifyAccount(username, password);
if (!result.isAuthenticated)
return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
Session["SID"] = result.userid;
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
Below is the structure of the AuthModelUserVerification
from the WCF:
public class AuthModel
{
public class UserVerification {
public int? userid { get; set; }
public bool isAuthenticated { get; set; }
public UserVerification()
{
userid = null;
isAuthenticated = false;
}
}
}
I am trying to make a unit test on VerifyAccount
method to test the status code being returned to the browser under certain conditions. I am using MSTest (.NET) and Fake it Easy mocking framework. The issue lies upon setting the value on the Session["SID"]
Session["SID"] = result.userid;
I am receiving the following error on this line when I debug the test:
Object reference not set to an instance of an object
While debugging the test, everytime I hover to the Session["SID"]
, it says null but the result.userid
shows it has a value of 1 since I am passing a value to it via calling the mock service I made. Please see the implementation of my test here:
private readonly AuthController _controller_Auth;
private readonly ILMS_Service _lms_service;
public Auth_UnitTest() {
_lms_service = A.Fake<ILMS_Service>();
_controller_Auth = new AuthController(_lms_service);
}
[TestMethod]
public void VerifyAccount_Success()
{
//Arrange
string username = "admin";
string password = "sampleP@sswoRd";
int userID = 1;
int expected_response_code = 200;
var session = A.Fake<HttpSessionStateBase>();
A.CallTo(() => session["SID"]).Returns(userID);
A.CallTo(() => _lms_service.VerifyAccount(username, password))
.Returns(new AuthModelUserVerification
{
userid = userID,
isAuthenticated = true
});
//Act
var result = _controller_Auth.VerifyAccount(username, password) as HttpStatusCodeResult;
//Assert
Assert.AreEqual(expected_response_code, result.StatusCode);
}
The mock is working since the isAuthenticated
has the value of true
when I debug it. It's the Session
that isn't working. Even making a fake HttpSessionStateBase
didn't resolve the issue. I am new to unit testing and I am still exploring things, any help would be apprecited. Thanks!
UPDATE
Based on @Blair Conrad's answer I ended up with this code on my test.
private readonly AuthController _controller_Auth;
private readonly ILMS_Service _lms_service;
private readonly HttpContextBase httpContext;
private readonly HttpResponseBase httpResponse;
private readonly HttpSessionStateBase httpSession;
public Auth_UnitTest()
{
// Mock WCF
_lms_service = A.Fake<ILMS_Service>();
// SUTs
_controller_Auth = new AuthController(_lms_service);
// Fake session
httpContext = A.Fake<HttpContextBase>();
httpResponse = A.Fake<HttpResponseBase>();
httpSession = A.Fake<HttpSessionStateBase>();
A.CallTo(() => httpContext.Response).Returns(httpResponse);
A.CallTo(() => httpContext.Session).Returns(httpSession);
}
[TestMethod]
public void VerifyAccount_Authorized()
{
//Arrange
string username = "admin";
string password = "sampleP@sswoRd";
int? userID = 1;
int expected_statusCode = (int)HttpStatusCode.OK;
string expected_description = "Authorized";
var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), _controller_Auth);
_controller_Auth.ControllerContext = context;
A.CallTo(() => _lms_service.VerifyAccount(username, password))
.Returns(new AuthModelUserVerification
{
userid = userID,
isAuthenticated = true,
isActive = true
});
//Act
var _authUser = _controller_Auth.VerifyAccount(username, password) as JsonResult;
dynamic result = _authUser.Data;
//Assert
Assert.AreEqual(expected_statusCode, result.statusCode);
Assert.AreEqual(expected_description, result.description);
}
From what I can understand, you need to fake the HttpContextBase
, HttpResponseBase
, and finally HttpSessionStateBase
to fake the session being used on my controller. This works wonderfully as it have the expected values. I made the fakes of these classes as global and faked them on my test constructor class. If someone found a potential issue on my implementation, please let me know. Thanks!
CodePudding user response:
I see session
being created and configured, but no connection between it and _controller_Auth
. I suspect the latter is using a Session property that is uninitialized. The faked session
object needs to be provided to the system under test.
I'm not an ASP user, but I think it's more complicated than how you injected your _lms_service. Faking Session in MVC and FakeItEasy (warning: link is up and down) has an example that I have not tried. I will lightly edit:
var sut = new HomeController();
var httpContext = A.Fake<HttpContextBase>();
var httpResponse = A.Fake<HttpResponseBase>();
var httpSession = A.Fake<HttpSessionStateBase>();
A.CallTo(() => httpContext.Response).Returns(httpResponse);
A.CallTo(() => httpContext.Session).Returns(httpSession);
var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), sut);
sut.ControllerContext = context;
// ...