使用自定义扩展方法模拟会话对象

时间:2017-10-30 11:29:53

标签: c# unit-testing session mocking asp.net-core-mvc

在我的asp.net core 2应用程序中,我有一个静态类来处理与会话的一些交互。例如:

AsyncRestTemplate

我还在会话对象中添加了一些扩展方法:

ListenableFuture

更新:这就是我试图模仿的方式:

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);
    }
}

我得到的错误:

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);
    }
}

问题是我尝试过很多方法来模拟会话及其扩展方法,但我还没有得到任何结果。很多方法都适用于.netframework而不是核心。

我使用xunit进行测试,使用Moq库来模拟对象。请告诉我如何模拟会话及其扩展方法。欣赏它。

2 个答案:

答案 0 :(得分:1)

问题与.NET Core无关,是模拟库的限制,你不能用Moq模拟扩展方法。

这可能会有所帮助:Mocking Extension Methods with Moq

答案 1 :(得分:1)

取决于抽象而非结核。创建一个Factory方法,为您提供IHttpContextAccessor抽象。

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);
    }
}

这将允许您在使用mock进行测试时替换抽象。

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

//...

更好的是,如果这位经理只需要会话,那么不要暴露超出需要的数量。

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);
    }
}

所以现在只需要为测试模拟显式依赖项。

根据ISession Extensions source code GetStringSetString也是扩展方法

您需要模拟ISession.TryGetValueISession.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();

//...