tiagodaraujo / HttpContextMoq

MIT License
34 stars 14 forks source link

How to set and get session value in unit test? #8

Open urehman-sopheon opened 1 year ago

urehman-sopheon commented 1 year ago

I am trying to add a unit test where the name and age value will be set and then retrieved from a session.

I have tried the following code and i get null value for name and ago.

[TestMethod] public void SetupSession_WhenIfCalled_ShouldReturnValue() {

     // Arrange
     var context = new HttpContextMoq.HttpContextMock();
     context.SetupSession();

     // Act
     context.SessionMock.SetString("Name", "Mike");
     context.SessionMock.SetInt32("Age", 21);

     // Assert
     context.Session.GetString("Name").Should().Be("Mike");
  }
spSlaine commented 1 year ago

Try

Sorry I use xUnit & Shouldly

using HttpContextMoq.Extensions;
using Microsoft.AspNetCore.Http;
using HttpContextMoq;
using Shouldly;
using System.Text;

namespace TestProject1
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            // Arrange
            var context = new HttpContextMock();
            context.SetupSession();

            var nameKey = "Name";
            var name = "Mike";
            var nameValue = Encoding.UTF8.GetBytes(name);

            var ageKey = "Age";
            var age =32;
            var ageValue =  new[] {
                                      (byte)(age >> 24),
                                      (byte)(0xFF & (age >> 16)),
                                      (byte)(0xFF & (age >> 8)),
                                      (byte)(0xFF & age)
                                  };

            context.SessionMock.Mock.Setup(session => session.TryGetValue(nameKey, out nameValue)).Returns(true);
            context.SessionMock.Mock.Setup(session => session.TryGetValue(ageKey, out ageValue)).Returns(true);

            // Act

            // Assert
            context.Session.GetString(nameKey).ShouldBe(name);
            context.Session.GetInt32(ageKey).ShouldBe(32);
        }
    }
}

You shouldn't really be trying to test writing and reading from a mocked object as all you are testing is the mock.

In your test setup you need to configure the Session Mock to return given values for each session key.

The problem is that all the nice Session methods e.g. GetString, GetInt32 are extension methods and you can not setup Moq to 'trap' these calls. With a bit of digging and jumping though the extension methods that converts things to and from byte arrays, if you actually setup the Session.TryGetValue method then you can intercept the calls for given Session keys. the code that you are testing will still calling the extension methods e.g. Session.GetString().

For completeness here is the extension methods which contains the Get|SetString & Get|SetInt32 methods

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;

namespace Microsoft.AspNetCore.Http
{
    /// <summary>
    /// Extension methods for <see cref="ISession"/>.
    /// </summary>
    public static class SessionExtensions
    {
        /// <summary>
        /// Sets an int value in the <see cref="ISession"/>.
        /// </summary>
        /// <param name="session">The <see cref="ISession"/>.</param>
        /// <param name="key">The key to assign.</param>
        /// <param name="value">The value to assign.</param>
        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);
        }

        /// <summary>
        /// Gets an int value from <see cref="ISession"/>.
        /// </summary>
        /// <param name="session">The <see cref="ISession"/>.</param>
        /// <param name="key">The key to read.</param>
        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];
        }

        /// <summary>
        /// Sets a <see cref="string"/> value in the <see cref="ISession"/>.
        /// </summary>
        /// <param name="session">The <see cref="ISession"/>.</param>
        /// <param name="key">The key to assign.</param>
        /// <param name="value">The value to assign.</param>
        public static void SetString(this ISession session, string key, string value)
        {
            session.Set(key, Encoding.UTF8.GetBytes(value));
        }

        /// <summary>
        /// Gets a string value from <see cref="ISession"/>.
        /// </summary>
        /// <param name="session">The <see cref="ISession"/>.</param>
        /// <param name="key">The key to read.</param>
        public static string? GetString(this ISession session, string key)
        {
            var data = session.Get(key);
            if (data == null)
            {
                return null;
            }
            return Encoding.UTF8.GetString(data);
        }

        /// <summary>
        /// Gets a byte-array value from <see cref="ISession"/>.
        /// </summary>
        /// <param name="session">The <see cref="ISession"/>.</param>
        /// <param name="key">The key to read.</param>
        public static byte[]? Get(this ISession session, string key)
        {
            session.TryGetValue(key, out var value);
            return value;
        }
    }
}
tiagodaraujo commented 8 months ago

Hi @urehman-sopheon

I agree that support for the session isn't great because it's difficult to mock a byte[] for string, int, bool, etc.

@spSlaine awesome work with amazing examples and very well explained 👏

I added better session support with a new implementation of ISession simulating an in-memory store, like we already have to Headers, Cookies, etc.

Check the samples here: https://github.com/tiagodaraujo/HttpContextMoq/blob/master/tests/HttpContextMoq.Samples/SessionSamples.cs

Thank you