Как выполнить модульное тестирование класса, основанного на HttpContext.GetGlobalResourceObject? - PullRequest
0 голосов
/ 06 июня 2018

Я пытаюсь добавить тесты в проект веб-форм.Есть статический метод для захвата строк из файлов ресурсов.Один из классов, которые я пытаюсь протестировать, основан на извлечении текста из файла ресурсов.

public static class MyStaticClass {
    public static string getText(String name)
    {
        String s = HttpContext.GetGlobalResourceObject("MyResources", name).ToString();
        return s;
    }
}

public class ClassUnderTest
{
    // returns: "Hey it's my text"
    private string _eg = MyStaticClass.getText("label_in_resources.resx_file")
}

class UnitTests
{
    [Test]
    public void TestMyClass()
    {
        ClassUnderTest _cut = new ClassUnderTest();
        // errors out because ClassUnderTest utilizes getText
        // which requires HttpContext.GetGlobalResourceObject
        // ... other stuff
    }
}

Примечание: это упрощенные примеры.

Проблема заключается в том, что я получаю тестОшибка с сообщением:

Сообщение: System.NullReferenceException: ссылка на объект не установлена ​​на экземпляр объекта.потому что HttpContext является нулевым во время этих тестов.

Я посмотрел довольно много SO сообщений о насмешливом HttpContext, но я не думаю, что полностью понимаю, что именно они делают, так как ониОбычно вы имеете дело с MVC, а не с Webforms.Тем не менее, большинство из них используют HttpContextBase и / или HttpContextWrapper, но, опять же, я не уверен, как их реализовать.

Также - я не тестирую метод getText напрямую.Я знаю, что это работает.Я тестирую класс, который его использует.Поможет ли насмешка над HttpContext в этой ситуации?

Я понимаю, что это своего рода гибрид модульного теста / интеграционного теста, так что если это не лучший способ, я всеуши ... или .. скорее глаза.

Редактировать

На данный момент я изменил свой метод getText, чтобы он возвращал ключ (имя), если результатHttpContext.GetGlobalResourceObject является нулевым.Затем я обновил свои тесты, чтобы ожидать ключ вместо значения.Это не идеально, но это работает и позволяет мне продолжать.Если есть лучший способ, пожалуйста, дайте мне знать.

public static class MyStaticClass {
    public static string getText(String name)
    {
        String s = HttpContext.GetGlobalResourceObject("MyResources", name);
        return s != null ? s.ToString() : name;
    }
}

1 Ответ

0 голосов
/ 06 июня 2018

Оригинальный ответ с Fakes (см. Ниже, чтобы разобраться с удалением статики)

Итак, есть одна оговорка, о которой я полностью забыл, пока не попытался это сделать.Я почти уверен, что Fakes по-прежнему требуется версия VS для Enterprise.Я не знаю, есть ли способ заставить его работать с NUnit, но когда вы не можете изменить код, иногда вам просто нужно разобраться с этим.

Вот пример Shimming вашей статикиметод.Вам не нужно беспокоиться о HttpContext (пока), поскольку вы не используете его напрямую.Вместо этого вы можете использовать метод getText(string).

Фактический бизнес-проект

namespace FakesExample
{
    public class MyStaticClass
    {
        public static string GetText(string name)
        {
            throw new NullReferenceException();
        }
    }
}

Ваш проект модульного тестирования

using System;
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FakesExampleTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            using (ShimsContext.Create())
            {
                FakesExample.Fakes.ShimMyStaticClass.GetTextString = (s) =>
                {
                    return "Go away null reference";
                };

                Console.WriteLine(FakesExample.MyStaticClass.GetText("foo"));
            }
        }
    }
}

Я действительно запускал это, так что я знаю, что это работает.Случается так, что даже если GetText всегда будет вызывать исключение NullReferenceException при вызове, наша Shim возвращает его собственное пользовательское сообщение.

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

В вашем модульном тестовом проекте щелкните правой кнопкой мыши по вашей ссылке и скажите «Добавить подделки».Он сгенерирует все прокладки и заглушки для вашей сборки.


Процесс удаления статического

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

Вот как я мог бы удалить статический объект и удалить зависимость от HttpContext

public interface IResourceRepository
{
    string Get(string name);
}

public class HttpContextResourceRepository : IResourceRepository
{
    public string Get(string name)
    {
        return HttpContext.GetGlobalResourceObject("MyResources", name).ToString();
    }
}

public class MyFormerStaticClass
{
    IResourceRepository _resourceRepository;

    public MyFormerStaticClass(IResourceRepository resourceRepository)
    {
        _resourceRepository = resourceRepository;
    }

    public string GetText(string name)
    {
        return _resourceRepository.Get(name);
    }
}

. Затем я использовал бы Dependency Injection для создания моих HttpContextResourceRepository и MyStaticClass (который, вероятно, также должен быть связан) в реальном бизнес-коде.

Для модульного теста я бы посмеялся над реализацией

[TestFixture]
public class UnitTest1
{
    [Test]
    public void TestMethod1()
    {
        var repoMock = new Mock<IResourceRepository>();
        repoMock.Setup(repository => repository.Get("foo")).Returns("My Resource Value");

        var formerStatic = new MyFormerStaticClass(repoMock.Object);
        Console.WriteLine(formerStatic.GetText("foo"));
    }
}

По этому маршруту вы можете создать любое количество реализаций IResourceRepository.и меняйте их местами в любое время.

...