Home > Software design >  Object reference not set to an instance of an object at ASP.NET MVC Session on Unit Testing using Fa
Object reference not set to an instance of an object at ASP.NET MVC Session on Unit Testing using Fa

Time:10-01

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;
// ...
  • Related