使用Moles在unit testing中模拟会话变量

方法我是unit testing检查会话变量之类的

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

在我的测试中,由于Session为null,因此无法摆脱此问题,因此抛出null引用exception。

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

 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 refexception。

任何想法如何模拟这个/在我的测试中传递这个[不使用任何外部DLL或主要代码更改。 对不起,但是不能这样做]。

非常感谢和帮助你。

2013年更新:

现在的坏消息是Moles框架是Microsoft Research(MSR)项目,并且在Visual Studio 2012中不受支持 。 好消息是,微软现已将MSR项目整合到主线框架中作为Microsoft Fakes 。

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

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

这是我之前回答的更新版本,它使用Fakes框架而不是Moles。

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

你甚至可以让它看起来更像我之前发布的Moles版本,尽管我还没有尝试过。 我只是根据我的答案调整文章的代码:)


2013年之前编辑:

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

我发现这个post描述了某人如何使用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(); 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 } } 

这将需要更改您的测试代码,但它是可测试性和代码可移植性的变化类型。