简体   繁体   English

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

[英]mocking session variable in unit test using Moles

Method I am unit testing checks for a session variable like 方法我是单元测试检查会话变量之类的

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

In my test, not able to get rid of this since Session is null, it's throwing null referrence exception. 在我的测试中,由于Session为null,因此无法摆脱此问题,因此抛出null引用异常。

To bypass this, I have tried mocking it like below but no luck 为了绕过这个,我试着像下面那样嘲笑但没有运气

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

I even tried method mention here to simulate HttpContext and then doing below 我甚至尝试过这里提到的方法来模拟HttpContext然后在下面做

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

But again no luck. 但再次没有运气。 This time, though the HttpContext.Current is not null but HttpContext.Current.Session and hence throws null ref exception. 这一次,虽然HttpContext.Current不是null但是HttpContext.Current.Session因此抛出null ref异常。

Any idea how can I mock this/by pass this in my test [Without using any external DLL or main code change. 任何想法如何模拟这个/通过我的测试通过[不使用任何外部DLL或主要代码更改。 Sorry, but can't afford to do so]. 对不起,但是不能这样做]。

Thanks and appreaciate lot your help. 非常感谢和帮助你。

Update 2013: 2013年更新:

The bad news now is that the Moles framework was a Microsoft Research (MSR) project, and will not be supported in Visual Studio 2012 . 现在的坏消息是Moles框架是Microsoft Research(MSR)项目,并且在Visual Studio 2012中不受支持 The great news is that Microsoft has now integrated the MSR project into the mainline framework as Microsoft Fakes . 好消息是,微软现已将MSR项目整合到主线框架中作为Microsoft Fakes

I found an article that solves the problem you had, using the Fakes framework instead of the Moles framework: 我找到了一篇文章,使用Fakes框架而不是Moles框架来解决您遇到的问题:

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

Here's an updated version of my previous answer that uses the Fakes framework instead of Moles. 这是我以前的答案的更新版本,它使用Fakes框架而不是Moles。

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

You might even be able to make it look more like the Moles version I posted before, though I haven't tried that out yet. 你甚至可以让它看起来更像我之前发布的Moles版本,尽管我还没有尝试过。 I'm just adapting the article's code to my answer :) 我只是根据我的答案调整文章的代码:)


Before 2013 edit: 2013年之前编辑:

You said you want to avoid changing the code under test. 你说你想避免更改测试中的代码。 While I think it should be changed, as directly accessing session state like that is a bad idea, I can understand where you're coming from (I was in test once...). 虽然我认为它应该被改变,因为直接访问会话状态是一个坏主意,我可以理解你来自哪里(我曾经在测试过......)。

I found this thread describing how someone moled both HttpContext and HttpSessionState to get around this problem . 我发现这个帖子描述了某人如何使用HttpContextHttpSessionState来解决这个问题

Their code ended up looking like this: 他们的代码最终看起来像这样:

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

I'd go even farther and implement ItemGetString with a dictionary: 我会更进一步,用字典实现ItemGetString

var sessionState = new Dictionary<string, object>();

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

Before edit: 编辑前:

I usually solve problems like this by encapsulating global state with an abstract class or interface that can be instanced and mocked out. 我通常通过使用抽象类或可以实例化和模拟的接口封装全局状态来解决这样的问题。 Then instead of directly accessing the global state, I inject an instance of my abstract class or interface into the code that uses it. 然后,我将抽象类或接口的实例注入到使用它的代码中,而不是直接访问全局状态。

This lets me mock out the global behavior, and makes it so my tests don't depend on or exercise that unrelated behavior. 这让我模拟了全局行为,并使我的测试不依赖于或行使不相关的行为。

Here's one way to do that (I'd play with the factoring a bit tho): 这是一种方法(我会稍微考虑因素):

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

This would require changes to your code-under-test, but it is the type of change that is good both for testability and for portability of the code. 这将需要更改您的测试代码,但它是可测试性和代码可移植性的变化类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM