8

How to Mock Session variables in ASP.net core unit testing project?

1) I have created a mock object of a session.

Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
Mock<ITestSession> mockSession = new Mock<ISession>().As<ITestSession>();

2) Setup GetString() MEthod

mockSession.Setup(s => s.GetString("ModuleId")).Returns("1");

3) created controllerContext and assigned mockhttpContext object

controller.ControllerContext.HttpContext = mockHttpContext.Object; 

4) Trying to read from a controller.

HttpContext.Session.GetString("ModuleId")

Whereas I get a null value of "ModuleId". Please help me to mock session GetString() method

Example:

        //Arrange
        //Note: Mock session 
        Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
        Mock<ITestSession> mockSession = new Mock<ISession>().As<ITestSession>();
        //Cast list to IEnumerable
        IEnumerable<string> sessionKeys = new string[] { };
        //Convert to list.
        List<string> listSessionKeys = sessionKeys.ToList();
        listSessionKeys.Add("ModuleId");
        sessionKeys = listSessionKeys;
        mockSession.Setup(s => s.Keys).Returns(sessionKeys);
        mockSession.Setup(s => s.Id).Returns("89eca97a-872a-4ba2-06fe-ba715c3f32be");
        mockSession.Setup(s => s.IsAvailable).Returns(true);
        mockHttpContext.Setup(s => s.Session).Returns(mockSession.Object);
     mockSession.Setup(s => s.GetString("ModuleId")).Returns("1");         

        //Mock TempData
        var tempDataMock = new Mock<ITempDataDictionary>();
        //tempDataMock.Setup(s => s.Peek("ModuleId")).Returns("1");

        //Mock service
        Mock<ITempServices> mockITempServices= new Mock<ITempServices>();
        mockITempServices.Setup(m => m.PostWebApiData(url)).Returns(Task.FromResult(response));

        //Mock Management class method
        Mock<ITestManagement> mockITestManagement = new Mock<ITestManagement>();
        mockITestManagement .Setup(s => s.SetFollowUnfollow(url)).Returns(Task.FromResult(response));

        //Call Controller method
        TestController controller = new TestController (mockITestManagement .Object, appSettings);
        controller.ControllerContext.HttpContext = mockHttpContext.Object;            
        controller.TempData = tempDataMock.Object;

        //Act
        string response = await controller.Follow("true");

        // Assert
        Assert.NotNull(response);
        Assert.IsType<string>(response);
 
3
  • Show the method under test. Commented Feb 16, 2017 at 10:16
  • Thank you, NKosi for your reply. Commented Feb 17, 2017 at 8:21
  • I got solution for this problem. I have created on mock class for session and inherited from ISession. Implemented all methods of ISession in this mock class and used this class to store session variables. Commented Feb 17, 2017 at 8:23

6 Answers 6

10

First create class Named mockHttpSession and inherit from ISession.

public class MockHttpSession : ISession
{
    Dictionary<string, object> sessionStorage = new Dictionary<string, object>();

    public object this[string name]
    {
        get { return sessionStorage[name]; }
        set { sessionStorage[name] = value; }
    }

    string ISession.Id
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    bool ISession.IsAvailable
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    IEnumerable<string> ISession.Keys
    {
        get { return sessionStorage.Keys; }
    }

    void ISession.Clear()
    {
        sessionStorage.Clear();
    }

    Task ISession.CommitAsync()
    {
        throw new NotImplementedException();
    }

    Task ISession.LoadAsync()
    {
        throw new NotImplementedException();
    }

    void ISession.Remove(string key)
    {
        sessionStorage.Remove(key);
    }

    void ISession.Set(string key, byte[] value)
    {
        sessionStorage[key] = value;
    }

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }        
}

Then use this session in actual controller:

     Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
        MockHttpSession mockSession = new MockHttpSession();           
        mockSession["Key"] = Value;
        mockHttpContext.Setup(s => s.Session).Returns(mockSession);
        Controller controller=new Controller();
        controller.ControllerContext.HttpContext = mockHttpContext.Object;
Sign up to request clarification or add additional context in comments.

3 Comments

We can use this session class to store session variable.For Example: First create object of mockHttpSession and then store variable value.
Don't use this code, people, please. ASCII.GetBytes(sessionStorage[key].ToString()); - it's reencoding bytes array with a different encoding!! I just spent half a day debugging a test someone wrote by copy-pasting this code... And I was wondering "why the heck my byte-array changes inside session?"
I ran into a similar thing as @AlexfromJitbit. Found the solution for it here, basically switch it to UTF8 and modify some logic to not double convert in that TryGetValue method: stackoverflow.com/questions/50277705/…
4

Solution given by Pankaj Dhote is working. here is complete bug free code for ASP.NET CORE 2 MVC:

public class MockHttpSession : ISession
{
    Dictionary<string, object> sessionStorage = new Dictionary<string, object>();
    public object this[string name]
    {
        get { return sessionStorage[name]; }
        set { sessionStorage[name] = value; }
    }

    string ISession.Id
    {
        get
        {
            throw new NotImplementedException();
        }
    }
    bool ISession.IsAvailable
    {
        get
        {
            throw new NotImplementedException();
        }
    }
    IEnumerable<string> ISession.Keys
    {
        get { return sessionStorage.Keys; }
    }
    void ISession.Clear()
    {
        sessionStorage.Clear();
    }
    Task ISession.CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        throw new NotImplementedException();
    }

    Task ISession.LoadAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        throw new NotImplementedException();
    }

    void ISession.Remove(string key)
    {
        sessionStorage.Remove(key);
    }

    void ISession.Set(string key, byte[] value)
    {
        sessionStorage[key] = value;
    }

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }
}

Then use this session in actual controller:

    Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
    MockHttpSession mockSession = new MockHttpSession();           
    mockSession["Key"] = Value;
    mockHttpContext.Setup(s => s.Session).Returns(mockSession);
    Controller controller=new Controller();
    controller.ControllerContext.HttpContext = mockHttpContext.Object;

Comments

3

I just ran into this problem recently and the only way out of it is to mock the function that the GetString method wraps, which is TryGetValue.

byte[] dummy = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
_mockSession.Setup(x => x.TryGetValue(It.IsAny<string>(),out dummy)).Returns(true).Verifiable();

So you don't need to mock the call to the GetString method, you just mock what method that calls behind the scenes.

1 Comment

I upvoted this because it did point me in the right direction but it's lacking some details needed to actually use it.
3

I used Pankaj Dhote's class for a mock ISession. I have to change one method from this:

bool ISession.TryGetValue(string key, out byte[] value)
{
    if (sessionStorage[key] != null)
    {
        value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
        return true;
    }
    else
    {
        value = null;
        return false;
    }
}  

to the code below. Otherwise the reference to sessionStorage[key].ToString() returns the name of the type rather than the value in the dictionary.

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = (byte[])sessionStorage[key]; //Encoding.UTF8.GetBytes(sessionStorage[key].ToString())
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

Comments

2

At first i created implementation of ISession:

public class MockHttpSession : ISession
{
    readonly Dictionary<string, object> _sessionStorage = new Dictionary<string, object>();
    string ISession.Id => throw new NotImplementedException();
    bool ISession.IsAvailable => throw new NotImplementedException();
    IEnumerable<string> ISession.Keys => _sessionStorage.Keys;
    void ISession.Clear()
    {
        _sessionStorage.Clear();
    }
    Task ISession.CommitAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
    Task ISession.LoadAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
    void ISession.Remove(string key)
    {
        _sessionStorage.Remove(key);
    }
    void ISession.Set(string key, byte[] value)
    {
        _sessionStorage[key] = Encoding.UTF8.GetString(value);
    }
    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (_sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(_sessionStorage[key].ToString());
            return true;
        }
        value = null;
        return false;
    }
}

Secondly implemented it during controller definition:

private HomeController CreateHomeController()
        {
            var controller = new HomeController(
                mockLogger.Object,
                mockPollRepository.Object,
                mockUserRepository.Object)
            {
                ControllerContext = new ControllerContext
                {
                    HttpContext = new DefaultHttpContext() {Session = new MockHttpSession()}
                }
            };
            return controller;
        } 

Comments

1

Following steps worked for me when I was working on Blazor server with .net 5 and wanted to test a component that was using ProtectedSessionStorage.

//add ProtectedSessionStorage

            testContext.Services.AddSingleton<Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.ProtectedSessionStorage>();



            //mock IJSRuntime

            var js = new Mock<IJSRuntime>();

            testContext.Services.AddSingleton(js.Object);



            //mock IDataProtector

            var mockDataProtector = new Mock<Microsoft.AspNetCore.DataProtection.IDataProtector>();

            mockDataProtector.Setup(sut => sut.Protect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("protectedText"));

            mockDataProtector.Setup(sut => sut.Unprotect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("originalText"));

            testContext.Services.AddSingleton(mockDataProtector.Object);



            //mock IDataProtectionProvider

            var mockDataProtectionProvider = new Mock<Microsoft.AspNetCore.DataProtection.IDataProtectionProvider>();

            mockDataProtectionProvider.Setup(s => s.CreateProtector(It.IsAny<string>())).Returns(mockDataProtector.Object);

            testContext.Services.AddSingleton(mockDataProtectionProvider.Object);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.