使用mole在单元测试中模拟会话变量

本文关键字:模拟 会话 变量 单元测试 mole 使用 | 更新日期: 2023-09-27 18:17:46

方法我正在单元测试检查会话变量,如

if(Session["somevar"] != null)
{
   // rest of the code
}

在我的测试中,无法摆脱这个,因为Session是空的,它抛出空引用异常。

为了绕过这个,我试着像下面这样嘲笑它,但是没有运气

System.Web.Moles.MHttpContext.AllInstances.SessionGet = (HttpContext cntx) =>
{ return (HttpSessionState)cntx.Session["somevar"]; }

我甚至尝试了这里提到的方法来模拟HttpContext,然后在

下面做
HttpContext.Current = new HttpContext(workerRequest);
HttpContext.Current.Session["somevar"] = value;

但是还是没有运气。这一次,虽然HttpContext.Current不是null,但HttpContext.Current.Session会抛出null ref异常。

不使用任何外部DLL或主代码更改,我如何模拟这个/通过我的测试。很抱歉,我们负担不起。

谢谢你的帮助。

使用mole在单元测试中模拟会话变量

2013年更新:

现在的坏消息是mole框架是微软研究院(MSR)的一个项目,在Visual Studio 2012中将不受支持。好消息是,微软现在已经将MSR项目集成到主线框架Microsoft Fakes中。

我找到了一篇文章,解决了你遇到的问题,使用Fakes框架而不是mole框架:

http://blog.christopheargento.net/2013/02/02/testing-untestable-code-thanks-to-ms-fakes/

这是我之前的答案的更新版本,使用了Fakes框架而不是mole。

using System.Web.Fakes;
// ...
var sessionState = new Dictionary<string, object>();
ShimHttpContext.CurrentGet = () => new ShimHttpContext();
ShimHttpContext.AllInstances.SessionGet = (o) => new ShimHttpSessionState
{
    ItemGetString = (key) =>
    {
        object result = null;
        sessionState.TryGetValue(key, out result);
        return result;
    }
};

你甚至可以让它看起来更像我之前发布的痣版本,虽然我还没有尝试过。我只是把文章的代码改编成我的答案:)


2013年前编辑:

你说你想要避免改变测试中的代码。虽然我认为它应该被改变,因为直接访问会话状态像这样是一个坏主意,我可以理解你来自哪里(我曾经在测试…)。

我发现这个线程描述了有人如何模拟HttpContextHttpSessionState来解决这个问题。

他们的代码最终看起来像这样:

MHttpContext.CurrentGet = () => new MHttpContext
{
    SessionGet = () => new MHttpSessionState
    {
        ItemGetString = (key) =>
        {
            if (key == "some")
                return "someString"/* or any other object*/;
            else return null;
        }
    }
};

我会更进一步,用字典实现ItemGetString:

var sessionState = new Dictionary<string, object>();
MHttpContext.CurrentGet = // ...
{
    // ...
    ItemGetString = (key) =>
    {
        object result = null;
        sessionState.TryGetValue(key, out result);
        return result;
    }

编辑前

:

我通常通过用一个可以实例化和模拟的抽象类或接口封装全局状态来解决这类问题。然后,我将抽象类或接口的实例注入到使用它的代码中,而不是直接访问全局状态。

这使我可以模拟全局行为,从而使我的测试不依赖或执行不相关的行为。

这里有一种方法可以做到这一点(我想玩一下分解):

public interface ISessionContext
{
    object this[string propertyName] { get; set; }
}
public class ServerContext : ISessionContext
{
    public object this[string propertyName]
    {
        get { return HttpContext.Current.Session[propertyName]; }
        set { HttpContext.Current.Session[propertyName] = value; }
    }
}
public class SomeClassThatUsesSessionState
{
    private readonly ISessionContext sessionContext;
    public SomeClassThatUsesSessionState(ISessionContext sessionContext)
    {
        this.sessionContext = sessionContext;
    }
    public void SomeMethodThatUsesSessionState()
    {
        string somevar = (string)sessionContext["somevar"];
        // todo: do something with somevar
    }
}

这将需要更改您的被测代码,但这种更改类型对代码的可测试性和可移植性都有好处。