Подтвердить что ты не робот

Как издеваться над статическими методами в С# с использованием среды MOQ?

Недавно я делал модульное тестирование, и я успешно издевался над различным сценарием с использованием рамок MOQ и тестов MS, создавая модульное тестирование. Поскольку я знаю, что мы не можем тестировать частные методы, но используя рефлексию, но я хочу знать, как мы можем протестировать и издеваться над модульными тестами с помощью среды MOQ.

4b9b3361

Ответ 1

Moq (и другие DynamicProxy основанные на смешении фреймворки) не могут издеваться над чем-либо, что не является виртуальным или абстрактным методом.

Запечатанные/статические классы/методы могут быть сфальсифицированы только с помощью инструментов на основе API-интерфейса Profiler, таких как Typemock (коммерческий) или Microsoft Moles (бесплатно, известный как Fakes in Visual Studio 2012 Ultimate).

В качестве альтернативы вы можете реорганизовать свой проект на абстрактные вызовы на статические методы и предоставить эту абстракцию вашему классу через инъекцию зависимостей. Тогда у вас будет не только лучший дизайн, но и будет проверен с помощью бесплатных инструментов, таких как Moq.

Можно использовать общий шаблон, позволяющий тестировать, без использования каких-либо инструментов. Рассмотрим следующий метод:

public class MyClass
{
    public string[] GetMyData(string fileName)
    {
        string[] data = FileUtil.ReadDataFromFile(fileName);
        return data;
    }
}

Вместо того, чтобы пытаться высмеять FileUtil.ReadDataFromFile, вы можете обернуть его в метод protected virtual, например:

public class MyClass
{
    public string[] GetMyData(string fileName)
    {
        string[] data = GetDataFromFile(fileName);
        return data;
    }

    protected virtual string[] GetDataFromFile(string fileName)
    {
        return FileUtil.ReadDataFromFile(fileName);
    }
}

Затем в unit test выведите MyClass и назовите его TestableMyClass. Затем вы можете переопределить метод GetDataFromFile, чтобы вернуть свои собственные тестовые данные.

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

Ответ 2

Другой вариант преобразования статического метода в статический Func или Action. Например.

Исходный код:

    class Math
    {
        public static int Add(int x, int y)
        {
            return x + y;
        }

Вы хотите "высмеять" метод "Добавить", но вы не можете. Измените код выше:

        public static Func<int, int, int> Add = (x, y) =>
        {
            return x + y;
        };

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

Теперь, из unit-теста, чтобы изменить поведение метода, просто переназначьте ему встроенную функцию:

    [TestMethod]
    public static void MyTest()
    {
        Math.Add = (x, y) =>
        {
            return 11;
        };

Поместите любую логику, которую вы хотите в методе, или просто верните какое-то жестко закодированное значение, в зависимости от того, что вы пытаетесь сделать.

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

[править] Я предлагаю добавить следующий код очистки в класс Unit Test:

    [TestCleanup]
    public void Cleanup()
    {
        typeof(Math).TypeInitializer.Invoke(null, null);
    }

Добавьте отдельную строку для каждого статического класса. Это означает, что после выполнения Unit Test он сбрасывает все статические поля обратно в исходное значение. Таким образом, другие юнит-тесты в том же проекте начнутся с правильных значений по умолчанию, в отличие от вашей изделенной версии.

Ответ 3

Moq не может издеваться над статическим членом класса.

При разработке кода для проверки важно избегать статических элементов (и одиночных чисел). Шаблон проектирования, который может помочь вам переформатировать ваш код для проверки, - это инъекция зависимостей.

Это означает следующее:

public class Foo
{
    public Foo()
    {
        Bar = new Bar();
    }
}

к

public Foo(IBar bar)
{
    Bar = bar;
}

Это позволяет использовать макет из ваших модульных тестов. В производстве вы используете инструмент Injection Dependency, например Ninject или Unity, который может подключать все вместе.

Я написал блог об этом некоторое время назад. Он объясняет, какие шаблоны используются для лучшего тестируемого кода. Может быть, это может вам помочь: Тестирование единиц, ад или небо?

Другим решением может быть использование Microsoft Fakes Framework. Это не замена для написания хорошо спроектированного тестируемого кода, но он может помочь вам. Структура Fakes позволяет вам издеваться над статическими членами и заменять их во время выполнения своим собственным пользовательским поведением.

Ответ 4

Как упоминалось в других ответах, MOQ не может издеваться над статическими методами и, как правило, следует избегать статики там, где это возможно.

Иногда это невозможно. Один работает с устаревшим или сторонним кодом или даже с использованием статических методов BCL.

Возможное решение - обернуть статику в прокси с интерфейсом, который можно высмеять

    public interface IFileProxy {
        void Delete(string path);
    }

    public class FileProxy : IFileProxy {
        public void Delete(string path) {
            System.IO.File.Delete(path);
        }
    }

    public class MyClass {

        private IFileProxy _fileProxy;

        public MyClass(IFileProxy fileProxy) {
            _fileProxy = fileProxy;
        }

        public void DoSomethingAndDeleteFile(string path) {
            // Do Something with file
            // ...
            // Delete
            System.IO.File.Delete(path);
        }

        public void DoSomethingAndDeleteFileUsingProxy(string path) {
            // Do Something with file
            // ...
            // Delete
            _fileProxy.Delete(path);

        }
    }

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

Другая возможность состоит в том, чтобы иметь "статический прокси" с различными реализациями интерфейса позади него

   public static class FileServices {

        static FileServices() {
            Reset();
        }

        internal static IFileProxy FileProxy { private get; set; }

        public static void Reset(){
           FileProxy = new FileProxy();
        }

        public static void Delete(string path) {
            FileProxy.Delete(path);
        }

    }

Теперь наш метод становится

    public void DoSomethingAndDeleteFileUsingStaticProxy(string path) {
            // Do Something with file
            // ...
            // Delete
            FileServices.Delete(path);

    }

Для тестирования мы можем установить свойство FileProxy в наш макет. Использование этого стиля уменьшает количество интерфейсов, которые нужно вводить, но делает зависимости менее очевидными (хотя и не более, чем исходные статические вызовы, которые, как я полагаю).