Модульный тест HttpContext.Current.Cache или другие методы на стороне сервера в C #? - PullRequest
33 голосов
/ 29 января 2009

При создании модульного теста для класса, который использует 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");
}

При вызове этого из модульного теста происходит сбой при значении NullReferenceException при обнаружении первой строки Cache. В Java я бы использовал Cactus для тестирования серверного кода. Есть ли подобный инструмент, который я могу использовать для кода C #? Этот вопрос SO упоминает фиктивные рамки - это единственный способ проверить эти методы? Есть ли подобный инструмент для запуска тестов на C #?

Кроме того, я не проверяю, является ли Cache нулевым, поскольку я не хочу писать код специально для модульного теста и предполагаю, что он всегда будет действительным при работе на сервере. Это допустимо, или я должен добавить нулевые проверки вокруг кэша?

Ответы [ 14 ]

42 голосов
/ 29 января 2009

Способ сделать это состоит в том, чтобы избежать прямого использования HttpContext или других подобных классов и заменить их на ложные. В конце концов, вы не пытаетесь проверить, что HttpContext работает правильно (это работа Microsoft), вы просто пытаетесь проверить, что методы были вызваны, когда они должны были.

Шаги (на случай, если вы просто хотите узнать технику, не копаясь в блогах):

  1. Создайте интерфейс, который описывает методы, которые вы хотите использовать в кэшировании (возможно, такие как GetItem, SetItem, ExpireItem). Назовите это ICache или как вам нравится

  2. Создайте класс, который реализует этот интерфейс и передает методы в реальный HttpContext

  3. Создайте класс, который реализует тот же интерфейс и просто действует как фиктивный кеш. Он может использовать словарь или что-то, если вы заботитесь о сохранении объектов

  4. Измените исходный код, чтобы он вообще не использовал HttpContext, а вместо этого использовал только ICache. Затем коду необходимо получить экземпляр ICache - вы можете либо передать экземпляр в конструктор классов (это все, чем на самом деле является внедрение зависимостей), либо вставить его в некоторую глобальную переменную.

  5. В вашем производственном приложении установите ICache в качестве реального HttpContext-Backed-Cache, а в модульных тестах установите ICache в качестве имитационного кэша.

  6. Profit!

29 голосов
/ 05 февраля 2009

Я согласен с другими, что использование интерфейса было бы лучшим вариантом, но иногда просто невозможно изменить существующую систему. Вот код, который я только что собрал вместе из одного из моих проектов, который должен дать вам результаты, которые вы ищете. Это самое красивое или красивое решение, но если вы действительно не можете изменить свой код, тогда оно должно быть выполнено.

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);
    }
}
16 голосов
/ 23 июля 2009
HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
6 голосов
/ 29 января 2009

Если вы используете .NET 3.5, вы можете использовать System.Web.Abstractions в своем приложении.

У Джастина Этереджа есть отличный пост о том, как издеваться над HttpContext (который содержит класс кэша)

Из примера Джастина я передаю HttpContextBase своим контроллерам, используя HttpContextFactory.GetHttpContext. Когда я их высмеиваю, я просто создаю Mock для вызовов кеша.

5 голосов
/ 30 июля 2013

Существует новый подход, помогающий разобраться конкретно с Cache в модульных тестах.

Я бы порекомендовал использовать новый MemoryCache.Default Microsoft. Вам нужно будет использовать .NET Framework 4.0 или более позднюю версию и включить ссылку на System.Runtime.Caching.

См. Статью здесь -> http://msdn.microsoft.com/en-us/library/dd997357(v=vs.100).aspx

MemoryCache.Default работает как для веб-приложений, так и для не веб-приложений. Поэтому идея заключается в том, чтобы обновить веб-приложение, чтобы удалить ссылки на HttpContext.Current.Cache и заменить их ссылками на MemoryCache.Default. Позже, когда вы решите использовать Unit Test для этих же методов, объект кеша все еще будет доступен и не будет иметь значение null. (Потому что он не зависит от HttpContext.)

Таким образом, вам даже не обязательно высмеивать компонент кэша.

2 голосов
/ 24 марта 2009

Вы можете использовать класс HttpContextBase в System.Web.Abstractions.dll. Это новая библиотека в .NET 3.5.

Вы можете найти пример использования по ссылке ниже.

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

2 голосов
/ 29 января 2009

По общему мнению, вождение всего, что связано с HttpContext в рамках модульного теста, - это настоящий кошмар, и его следует избегать, если это возможно.

Я думаю, вы на правильном пути в отношении насмешек. Мне нравится RhinoMocks (http://ayende.com/projects/rhino-mocks.aspx).

Я тоже прочитал несколько хороших вещей о MoQ (http://code.google.com/p/moq),, хотя я еще не пробовал.

Если вы действительно хотите писать веб-интерфейсы, тестируемые модульно, на C #, то, как люди, похоже, стремятся, это использовать инфраструктуру MVC (http://www.asp.net/mvc), а не WebForms ...

1 голос
/ 16 апреля 2014

, если вам нет дела до тестирования кэша, вы можете сделать это ниже:

[TestInitialize]
    public void TestInit()
    {
      HttpContext.Current = new HttpContext(new HttpRequest(null, "http://tempuri.org", null), new HttpResponse(null));
    }

Также вы можете moq, как показано ниже

var controllerContext = new Mock<ControllerContext>();
      controllerContext.SetupGet(p => p.HttpContext.Session["User"]).Returns(TestGetUser);
      controllerContext.SetupGet(p => p.HttpContext.Request.Url).Returns(new Uri("http://web1.ml.loc"));
1 голос
/ 28 мая 2009

Это может быть на вашей улице ... Фил Хаак показывает, с помощью насмешек Rhino, как имитировать httpcontext в asp mvc, но я думаю, что он может быть применен к веб-формам.

Clicky !!

Надеюсь, это поможет.

0 голосов
/ 12 августа 2016

Можно попробовать ...

 Isolate.WhenCalled(() => HttpContext.Current).ReturnRecursiveFake();
 var fakeSession = HttpContext.Current.Session;
 Isolate.WhenCalled(() => fakeSession.SessionID).WillReturn("1");
...