Переменная сеанса mocking в модульном тесте с использованием Moles - PullRequest
3 голосов
/ 13 декабря 2011

Метод Я проверяю модульные тесты для переменной сеанса, такой как

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 не является нулевым, но HttpContext.Current.Session и, следовательно, выдает исключение нулевого ref.

Любая идея, как я могу смутить это / мимо проходить это в моем тесте [Без использования каких-либо внешних DLL или изменения основного кода. Извините, но не могу себе этого позволить.]

Спасибо и высоко ценим вашу помощь.

1 Ответ

4 голосов
/ 13 декабря 2011

Обновление 2013:

Плохая новость заключается в том, что платформа Moles была проектом Microsoft Research (MSR), и не будет поддерживаться в Visual Studio 2012 .Хорошая новость заключается в том, что Microsoft теперь интегрировала проект MSR в основную инфраструктуру как Microsoft Fakes .

Я нашел статью, которая решает вашу проблему, используя вместо нее среду Fakes.Структура Moles:

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

Вот обновленная версия моего предыдущего ответа, в которой вместо Moles используется структура Fakes.

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

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


Перед редактированием 2013 года:

Вы сказали, что хотите избежать изменения тестируемого кода.Хотя я думаю, что следует изменить , так как прямой доступ к состоянию сеанса - плохая идея, я могу понять, откуда вы пришли (я был на тесте однажды ...).

Я нашел этот поток, описывающий, как кто-то пытался обойти эту проблему HttpContext и HttpSessionState .

Их код в итоге выглядел так:

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

Это потребует изменений в вашем коде.проходит тестирование, но это тип изменений, который хорош как для тестируемости, так и для переносимости кода.

...