0

In my asp.net core 2 application, I have a static class which handles some interactions with the session. For example :

public static class BookManager
{
    private const string Key = "Books";
    public static HttpContextAccessor ContextAccessor => new HttpContextAccessor();

    public static void AddBooksToContainer(IEnumerable<BookViewModel> books)
    {
        ContextAccessor.HttpContext.Session.Set(Key, books);
    }

    public static IEnumerable<BookViewModel> GetBooks()
    {
        return ContextAccessor.HttpContext.Session.Get<IEnumerable<BookViewModel>>(Key);
    }
}

I have also added some extension methods to the session object :

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, IEnumerable<T> value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));
    }

    public static T Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default(T) :
            JsonConvert.DeserializeObject<T>(value);
    }
}

Update: This is how i try to mock :

         var bookList = new List<BookViewModel>()
        {
            new BookViewModel {Author = "test", Id = Guid.NewGuid(), InStock = 1, Price = 1000, Title = "tes"}
        };

        var httpContextAccessor = new Mock<IHttpContextAccessor>().SetupAllProperties();
        httpContextAccessor.Setup(x => x.HttpContext.Session.Get<IEnumerable<BookViewModel>>("test")).Returns(bookList);

And the error i get :

System.NotSupportedException : Invalid setup on an extension method: x => 
x.HttpContext.Session.Get<IEnumerable`1>("test")

The problem is I have tried many ways to mock session and its extension methods but I got no result yet. many ways work in .netframework and not core.

I use xunit for testing and Moq library to mock the objects. Please tell me how can I mock session and its extension methods. Appreciate it.

peyman gilmour
  • 1,168
  • 2
  • 16
  • 35
  • 2
    Depend on abstractions and not concretions. Create a Factory method that will provide you with the `IHttpContextAccessor` abstraction. This will allow you to replace the abstraction when testing with a mock. – Nkosi Oct 30 '17 at 11:45
  • Show how you have tried to test it so far. – Nkosi Oct 30 '17 at 11:49
  • @Nkosi , I updated the post with the piece of code which shows how i try to mock but it gives me this error : System.NotSupportedException : Invalid setup on an extension method: x => x.HttpContext.Session.Get("test") – peyman gilmour Oct 30 '17 at 12:14
  • 1
    You can't mock the extension methods. You can however mock the body of the extension method. Provided they are not extension methods themselves. – Nkosi Oct 30 '17 at 12:16

2 Answers2

1

The problem is not related to .NET Core, is a limitation of the mocking library, you can't mock extension methods with Moq.

This might help: Mocking Extension Methods with Moq

Tao Gómez Gil
  • 2,228
  • 20
  • 36
1

Depend on abstractions and not concretions. Create a Factory method that will provide you with the IHttpContextAccessor abstraction.

public static class BookManager {
    private const string Key = "Books";
    public static Func<IHttpContextAccessor> ContextAccessor = () => new HttpContextAccessor();

    public static void AddBooksToContainer(IEnumerable<BookViewModel> books) {
        ContextAccessor().HttpContext.Session.Set(Key, books);
    }

    public static IEnumerable<BookViewModel> GetBooks() {
        return ContextAccessor().HttpContext.Session.Get<IEnumerable<BookViewModel>>(Key);
    }
}

This will allow you to replace the abstraction when testing with a mock.

//Arrange
var mock = mock.Of<IHttpContextAccessor>();
BookManager.ContextAccessor = () => {
    return mock.Object;
};

//...

Better yet if this manager only has need of the session then do not expose more than it needs.

public static class BookManager {
    private const string Key = "Books";
    public static Func<ISession> Session = () => new HttpContextAccessor().HttpContext.Session;

    public static void AddBooksToContainer(IEnumerable<BookViewModel> books) {
        Session().Set(Key, books);
    }

    public static IEnumerable<BookViewModel> GetBooks() {
        return Session().Get<IEnumerable<BookViewModel>>(Key);
    }
}

So now only the explicit dependencies need to be mocked for tests.

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

You will need to mock ISession.TryGetValue and ISession.Set

//Arrange
var mock = Mock.Of<ISession>();
BookManager.Session = () => {
    return mock;
};

var value = Encoding.UTF8.GetBytes("[{some books json strings here}]");

Mock.Get(mock).Setup(_ => _.TryGetValue("Books", out value))
    .Returns(true);

//Act
var books = BookManager.GetBooks();

//...
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Thanks for the help. The only problem is in last part ( setup ). I get Invalid setup on an extension method: _ => _.GetString("Books") . and as you said we can not mock extension methods. Is there any way to fix this part? – peyman gilmour Oct 30 '17 at 12:55
  • 1
    @peymangilmour that that means that `GertString` is also an extension method. You would need to find the source code of that method and see what it uses so you know what to mock. – Nkosi Oct 30 '17 at 12:59
  • 1
    @peymangilmour they are in fact extension methods https://learn.microsoft.com/en-us/dotnet/api/Microsoft.AspNetCore.Http.ISession?view=aspnetcore-2.0#Extension_Methods – Nkosi Oct 30 '17 at 13:19