6

I am writing unit tests using moq framework. I am calling one method in base controller setsession() which will set session using SetString("userdata",object) and I have one more method getsession() which will get session.

var sessionMock = new Mock<ISession>();
sessionMock.Setup(s => s.GetString("userdata")).Returns(Object);--failing
sessionMock.Setup(s => s.SetString("userdata",object));--failing

I am getting the error in s.GetString and s.SetString.

As GetString and SetString are extension methods may be I need to use another way to handle it.

Can you please help me?

4 Answers 4

17
+50

According to ISession Extensions source code GetString and SetString are extension methods

public static void SetString(this ISession session, string key, string value)
{
    session.Set(key, Encoding.UTF8.GetBytes(value));
}

public static string GetString(this ISession session, string key)
{
    var data = session.Get(key);
    if (data == null)
    {
        return null;
    }
    return Encoding.UTF8.GetString(data);
}

public static byte[] Get(this ISession session, string key)
{
    byte[] value = null;
    session.TryGetValue(key, out value);
    return value;
}

You will need to mock ISession.Set and ISession.TryGetValue in order to let the extension methods execute as expected.

//Arrange
var sessionMock = new Mock<ISession>();
var key = "userdata";
var value = new byte[0];

sessionMock.Setup(_ => _.Set(key, It.IsAny<byte[]>()))
    .Callback<string, byte[]>((k,v) => value = v);

sessionMock.Setup(_ => _.TryGetValue(key, out value))
    .Returns(true);
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks so much. its working as expected for mocking but now i have another problem.Below is my code for setsession() {var str = JsonConvert.SerializeObject(model);HttpContext.Session.SetString("userdata", str);} public sessionmodel GetUserSession() { var str = HttpContext.Session.GetString("userdata"); sessionmodel model = new sessionmodel (); if(string.IsNullOrEmpty(str)) { return null; } model = JsonConvert.DeserializeObject<sessionmodel>(str);--getting error here }
In setsession() method i am serialzing the object and in getsession() i am deserializing the object. While mocking the session object we used byte[] so thats the reason it is showing error while unit test. Please help
Just one little typo it should be v=value in the _.Set, also really thank you for this answer you helped me a lot. Could you explain me how you find the code for this methods in Git Hub I tried multiple things and can't find it.
@mybirthname no typo. value is the local variable in the test. the callback will set it based on what was passed into the invoked member. as for finding the code, it is like following bread crumbs. I would first try to find it in the API docs to get an idea of what namespace it belongs to and then go looking for that in the repository.
2

Thanks Nkosi for the great answer. In my case I had to deal with GetInt32 and SetInt32 extension methods in the unit test. As per the source code below is the implementation

public static void SetInt32(this ISession session, string key, int value)
        {
            var bytes = new byte[]
            {
                (byte)(value >> 24),
                (byte)(0xFF & (value >> 16)),
                (byte)(0xFF & (value >> 8)),
                (byte)(0xFF & value)
            };
            session.Set(key, bytes);
        }

public static int? GetInt32(this ISession session, string key)
        {
            var data = session.Get(key);
            if (data == null || data.Length < 4)
            {
                return null;
            }
            return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
        }

so following what's mentioned by as answer I had to modify little bit as below to handle it.

 Mock<ISession> sessionMock = new Mock<ISession>();
        var key = "FY";
        int fy = 2020;
        var value =  new byte[]
        {
            (byte)(fy >> 24),
            (byte)(0xFF & (fy >> 16)),
            (byte)(0xFF & (fy >> 8)),
            (byte)(0xFF & fy)
        };

  sessionMock.Setup(_ => _.TryGetValue(key, out value)).Returns(true);

I couldn't find this anywhere so sharing here in case it is helpful for someone who are having same issue.

Comments

1

here's my soultion to question

) create a new mock ISession & new mock HttpContext

//Arrange
var mockContext = new Mock<HttpContext>();
var mockSession = new Mock<ISession>();

) create session value

SomeClass sessionUser = new SomeClass() { Property1 = "MyValue" };
var sessionValue = JsonConvert.SerializeObject(sessionUser);
byte[] dummy = System.Text.Encoding.UTF8.GetBytes(sessionValue);

) setup mocks to return serialized obj

mockSession.Setup(x => x.TryGetValue(It.IsAny<string>(), out dummy)).Returns(true); //the out dummy does the trick
mockContext.Setup(s => s.Session).Returns(mockSession.Object);

) inject to mvc httpcontext (in my case a Page instead of a typical controller)

_classUnderTest.PageContext = new Microsoft.AspNetCore.Mvc.RazorPages.PageContext();
_classUnderTest.PageContext.HttpContext = mockContext.Object;

Now all is up and running, when i hit the session getvalue i receive that serializes object

Greez Nic

Comments

0

Extending the solution of NKosi to be able to mock any type of object stored in session, first create extensionmethods GetObject and SetObject

public static class SessionExtensions
{
    public static T GetObject<T>(this ISession session, string key)
    {
        var data = session.GetString(key);
        if (data == null)
        {
            return default(T);
        }
        return JsonConvert.DeserializeObject<T>(data);
    }

    public static void SetObject(this ISession session, string key, object value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }
}

Then create a TestHelper method like this:

public static void SetSession(Controller controller, string key = null, object testObject = null)
{
    controller.ControllerContext.HttpContext = new DefaultHttpContext();
    var mockSession = new Mock<ISession>();            
        
    if(testObject != null && key != null)
    {
        var value = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(testObject));
        mockSession.Setup(s => s.Set(key, It.IsAny<byte[]>())).Callback<string, byte[]>((k, v) => value = v);
        mockSession.Setup(s => s.TryGetValue(key, out value)).Returns(true);
    }
    controller.HttpContext.Session = mockSession.Object;
}

Imagine a controller which retreives a custom object from Session like this:

public IActionResult TestSessionObjects()
{
    .....
    var test = HttpContext.Session.GetObject<CustomObject>("KeyInSession");
    .....
    return View();
}

Now you can create a test:

[Fact]
public async void TestSessionObjectsTest()
{
    // Arrange                        
    var myController = new MyController();
    TestHelpers.SetSession(myController, "KeyInSession", new CustomObject());

    // Act
    ViewResult result = await myController.TestSessionObjects() as ViewResult;

    // Assert
    ....
}

Now when in your method under test GetObject is called, it will return the object created in the Arrange part.

HttpContext.Session.GetObject<CustomObject>("KeyInSession")

This will also work for e.g. int32. Set it up like:

[Fact]
public async void TestSessionObjectsTestInt()
{
    // Arrange                        
    var myController = new MyController();
    TestHelpers.SetSession(myController, "AnInt32", 2022);
    ....         
}

When in your method under test the GetObject is called 2022 will be returned:

HttpContext.Session.GetObject<int>("AnInt32")

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.