Reputation: 189
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);
Upvotes: 8
Views: 11588
Reputation: 1297
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);
Upvotes: 1
Reputation: 51
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;
}
Upvotes: 2
Reputation: 59
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;
}
}
Upvotes: 3
Reputation: 189
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;
Upvotes: 10
Reputation: 143
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;
Upvotes: 4
Reputation: 1988
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.
Upvotes: 3