单元测试 HttpContext.Current.Cache 或 C# 中的其他服务器端方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/489819/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 05:24:05  来源:igfitidea点击:

Unit test HttpContext.Current.Cache or other server-side methods in C#?

c#unit-testingnunitmocking

提问by Tai Squared

When creating a unit test for a class that uses the HttpContext.Current.Cacheclass, I get an error when using NUnit. The functionality is basic - check if an item is in the cache, and if not, create it and put it in:

当创建一个使用的类的单元测试HttpContext.Current.Cache,我使用NUnit时得到一个错误。功能是基本的 - 检查一个项目是否在缓存中,如果没有,创建它并将其放入:

if (HttpContext.Current.Cache["Some_Key"] == null) {
    myObject = new Object();
    HttpContext.Current.Cache.Insert("Some_Key", myObject);
}
else {
    myObject = HttpContext.Current.Cache.Get("Some_Key");
}

When calling this from a unit test, it fails with at NullReferenceExceptionwhen encountering the first Cacheline. In Java, I would use Cactusto test server-side code. Is there a similar tool I can use for C# code? This SO questionmentions mock frameworks - is this the only way I can test these methods? Is there a similar tool to run tests for C#?

当从单元测试中调用它NullReferenceException时,它在遇到第一Cache行时失败。在 Java 中,我会使用Cactus来测试服务器端代码。是否有类似的工具可以用于 C# 代码? 这个 SO 问题提到了模拟框架 - 这是我可以测试这些方法的唯一方法吗?是否有类似的工具来运行 C# 测试?

Also, I don't check if the Cacheis null as I don't want to write code specifically for the unit test and assume it will always be valid when running on a server. Is this valid, or should I add null checks around the cache?

此外,我不检查是否Cache为空,因为我不想专门为单元测试编写代码,并假设它在服务器上运行时始终有效。这是有效的,还是应该在缓存周围添加空检查?

采纳答案by Orion Edwards

The way to do this is to avoid direct use of the HttpContext or other similar classes, and substitute them with mocks. After all, you're not trying to test that the HttpContext functions properly (that's microsoft's job), you're just trying to test that the methods got called when they should have.

这样做的方法是避免直接使用 HttpContext 或其他类似的类,并用模拟代替它们。毕竟,您不是在尝试测试 HttpContext 是否正常运行(这是微软的工作),您只是在尝试测试方法是否在应该调用时被调用。

Steps (In case you just want to know the technique without digging through loads of blogs):

步骤(如果您只是想了解该技术而无需浏览大量博客):

  1. Create an interface which describes the methods you want to use in your caching (probably things like GetItem, SetItem, ExpireItem). Call it ICache or whatever you like

  2. Create a class which implements that interface, and passes methods through to the real HttpContext

  3. Create a class which implements the same interface, and just acts like a mock cache. It can use a Dictionary or something if you care about saving objects

  4. Change your original code so it doesn't use the HttpContext at all, and instead only ever uses an ICache. The code will then need to get an instance of the ICache - you can either pass an instance in your classes constructor (this is all that dependency injection really is), or stick it in some global variable.

  5. In your production app, set the ICache to be your real HttpContext-Backed-Cache, and in your unit tests, set the ICache to be the mock cache.

  6. Profit!

  1. 创建一个接口来描述您要在缓存中使用的方法(可能是 GetItem、SetItem、ExpireItem 之类的东西)。称之为 ICache 或任何你喜欢的

  2. 创建一个实现该接口的类,并将方法传递给真正的 HttpContext

  3. 创建一个实现相同接口的类,就像模拟缓存一样。如果您关心保存对象,它可以使用字典或其他东西

  4. 更改您的原始代码,使其根本不使用 HttpContext,而只使用 ICache。然后代码需要获取 ICache 的一个实例 - 您可以在类构造函数中传递一个实例(这就是依赖注入的全部内容),或者将其粘贴在某个全局变量中。

  5. 在您的生产应用程序中,将 ICache 设置为您真正的 HttpContext-Backed-Cache,并在您的单元测试中,将 ICache 设置为模拟缓存。

  6. 利润!

回答by Antony Perkov

General consensus seems to be that driving anything HttpContext related from within a unit test is a total nightmare, and should be avoided if possible.

普遍的共识似乎是,从单元测试中驱动任何与 HttpContext 相关的东西都是一场彻头彻尾的噩梦,应该尽可能避免。

I think you're on the right path regarding mocking. I like RhinoMocks (http://ayende.com/projects/rhino-mocks.aspx).

我认为您在嘲笑方面走在正确的道路上。我喜欢 RhinoMocks ( http://ayende.com/projects/rhino-mocks.aspx)。

I've read some good things about MoQ too (http://code.google.com/p/moq), although I've not tried it yet.

我也读过一些关于 MoQ 的好文章(http://code.google.com/p/moq),虽然我还没有尝试过。

If you really want to write unit testable web UIs in C#, the way people seem to be heading is to use the MVC framework (http://www.asp.net/mvc) rather than WebForms...

如果您真的想用 C# 编写可单元测试的 Web UI,人们似乎正在使用的方式是使用 MVC 框架(http://www.asp.net/mvc)而不是 WebForms...

回答by Steve Wright

If you're using .NET 3.5, you can use System.Web.Abstractions in your application.

如果您使用 .NET 3.5,则可以在应用程序中使用 System.Web.Abstractions。

Justin Etheredge has a great poston how to mock HttpContext (which contains the cache class).

贾斯汀Etheredge有一个伟大的职位上如何嘲笑的HttpContext(其中包含缓存类)。

From Justin's example, I pass an HttpContextBase to my controllers using the HttpContextFactory.GetHttpContext. When mocking them, I just build a Mock to make calls to the cache object.

在 Justin 的示例中,我使用 HttpContextFactory.GetHttpContext 将 HttpContextBase 传递给我的控制器。在模拟它们时,我只是构建了一个 Mock 来调用缓存对象。

回答by Rine

All of these programming questions ask for an interface based programming model, in which you implement the interface twice. One for the real code and one for the mockup.

所有这些编程问题都要求基于接口的编程模型,在该模型中您实现接口两次。一种用于真实代码,一种用于模型。

Instantiation is then the next issue. There are several design patterns that can be used for that. See for example the famous GangOfFour Creational patterns (GOF) or the Dependency Injection patterns.

实例化是下一个问题。有几种设计模式可用于此目的。例如,参见著名的 GangOfFour Creational patterns ( GOF) 或 Dependency Injection 模式。

ASP.Net MVC is in fact using this interface-based approach and is therefore much more suitable for unit testing.

ASP.Net MVC 实际上使用了这种基于接口的方法,因此更适合于单元测试。

回答by Brian Surowiec

I agree with the others that using an interface would be the best option but sometimes it's just not feasible to change an existing system around. Here's some code that I just mashed together from one of my projects that should give you the results you're looking for. It's the farthest thing from pretty or a great solution but if you really can't change your code around then it should get the job done.

我同意其他人的看法,即使用界面将是最好的选择,但有时改变现有系统是不可行的。这是我刚刚从我的一个项目中混合在一起的一些代码,它们应该可以为您提供您正在寻找的结果。这是离漂亮或出色的解决方案最远的事情,但是如果您真的无法更改代码,那么它应该可以完成工作。

using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;

[TestFixture]
public class HttpContextCreation
{
    [Test]
    public void TestCache()
    {
        var context = CreateHttpContext("index.aspx", "http://tempuri.org/index.aspx", null);
        var result = RunInstanceMethod(Thread.CurrentThread, "GetIllogicalCallContext", new object[] { });
        SetPrivateInstanceFieldValue(result, "m_HostContext", context);

        Assert.That(HttpContext.Current.Cache["val"], Is.Null);

        HttpContext.Current.Cache["val"] = "testValue";
        Assert.That(HttpContext.Current.Cache["val"], Is.EqualTo("testValue"));
    }

    private static HttpContext CreateHttpContext(string fileName, string url, string queryString)
    {
        var sb = new StringBuilder();
        var sw = new StringWriter(sb);
        var hres = new HttpResponse(sw);
        var hreq = new HttpRequest(fileName, url, queryString);
        var httpc = new HttpContext(hreq, hres);
        return httpc;
    }

    private static object RunInstanceMethod(object source, string method, object[] objParams)
    {
        var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
        var type = source.GetType();
        var m = type.GetMethod(method, flags);
        if (m == null)
        {
            throw new ArgumentException(string.Format("There is no method '{0}' for type '{1}'.", method, type));
        }

        var objRet = m.Invoke(source, objParams);
        return objRet;
    }

    public static void SetPrivateInstanceFieldValue(object source, string memberName, object value)
    {
        var field = source.GetType().GetField(memberName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
        if (field == null)
        {
            throw new ArgumentException(string.Format("Could not find the private instance field '{0}'", memberName));
        }

        field.SetValue(source, value);
    }
}

回答by Vadim

You can use HttpContextBase class in System.Web.Abstractions.dll. It's a new dll in .NET 3.5.

您可以在 System.Web.Abstractions.dll 中使用 HttpContextBase 类。这是 .NET 3.5 中的新 dll。

You can find an example how to use in the link below.

您可以在下面的链接中找到如何使用的示例。

http://vkreynin.wordpress.com/2009/03/23/stub-htttpcontext/

http://vkreynin.wordpress.com/2009/03/23/stub-htttpcontext/

回答by Vadim

As everyone said here, there's a problem with HTTPContext, currently Typemockis the only framework that can fake it directly without any wrappers or abstractions.

正如这里每个人所说的,HTTPContext 存在问题,目前Typemock是唯一可以直接伪造它而无需任何包装器或抽象的框架。

回答by WestDiscGolf

This maybe up your street ... Phil Haack shows off, with the help of Rhino mocks, how to mock httpcontext in asp mvc but I'd imagine can be applied to webforms.

这可能在你的街道上...... Phil Haack 在 Rhino mocks 的帮助下展示了如何在 asp mvc 中模拟 httpcontext 但我想可以应用于 webforms。

Clicky!!

咔嚓!!

Hope this helps.

希望这可以帮助。

回答by Chris

The cache object is difficult to mock because it's a sealed area of the .NET framework. I typically get around this by building a cache wrapper class that accepts a cache manager object. For testing, I use a mock cache manager; for production I use a cache manager that actually accesses HttpRuntime.Cache.

缓存对象很难模拟,因为它是 .NET 框架的一个封闭区域。我通常通过构建一个接受缓存管理器对象的缓存包装类来解决这个问题。为了测试,我使用了一个模拟缓存管理器;对于生产,我使用实际访问 HttpRuntime.Cache 的缓存管理器。

Basically, I abstract away the cache myself.

基本上,我自己抽象了缓存。

回答by Shawn Miller

HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));