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

Как я это издеваюсь?

В приложении .NET windows у меня есть класс с именем EmployeeManager. При создании экземпляра этот класс загружает сотрудников в список из базы данных, которая еще не завершила регистрацию. Я хотел бы использовать EmployeeManager в unit test. Однако я не хочу привлекать базу данных.

Из того, что я понимаю об этом сценарии, мне нужен интерфейс IEmployeeManager, который используется только для целей тестирования. Это не кажется правильным, поскольку интерфейс не имеет другого смысла. Тем не менее, это позволит мне создать тестовый класс EmployeeManager, который загружает сотрудников без привлечения базы данных. Таким образом, я могу назначить значения, которые в противном случае исходили из базы данных.

Правильно ли это, и нужно ли его исправлять? Mocking (Moq framework), кажется, использует много кода, просто для того, чтобы делать простые вещи, такие как присвоение свойства. Я не понимаю. Почему макет, когда я могу просто создать простой тестовый класс из IEmployeeManager, который предоставит мне то, что мне нужно?

4b9b3361

Ответ 1

Из того, что я понимаю об этом сценарии, мне нужен интерфейс IEmployeeManager, который используется только для целей тестирования. Это не кажется правильным, поскольку интерфейс не имеет другого смысла.

Хорошо стоит создать интерфейс. Также обратите внимание, что интерфейс фактически имеет несколько целей:

  • Интерфейс идентифицирует роли или обязанности, предоставляемые актером. В этом случае интерфейс идентифицирует роли и обязанности EmployeeManager. Используя интерфейс, вы предотвращаете случайную зависимость от конкретной базы данных.
  • Интерфейс уменьшает связь. Поскольку ваше приложение не будет зависеть от EmployeeManager, вы можете поменять его реализацию, не перекомпилируя остальную часть приложения. Конечно, это зависит от структуры проекта, количества сборок и т.д., Но тем не менее позволяет использовать этот тип повторного использования.
  • Интерфейс способствует тестируемости.. Когда вы используете интерфейс, гораздо проще создавать динамические прокси, которые позволяют более легко тестировать ваше программное обеспечение.
  • Интерфейс заставляет мысли 1. Хорошо, я уже упоминал об этом, но это стоит повторить. Просто используя только интерфейс, вы должны подумать о ролях и обязанностях объектов. Интерфейс не должен быть кухонной раковиной. Интерфейс представляет собой сплоченный набор ролей и обязанностей. Если методы интерфейса не являются сплоченными или почти не используются вместе, то, вероятно, объект имеет несколько ролей. Хотя это и не обязательно плохо, это означает, что несколько различных интерфейсов лучше. Чем больше интерфейс, тем сложнее сделать его ковариантным или контравариантным и, следовательно, более податливым в коде.

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

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

interface IEmployeeManager {
    void AddEmployee(ProspectiveEmployee e);
    void RemoveEmployee(Employee e);
}

class HiringOfficer {
    private readonly IEmployeeManager manager
    public HiringOfficer(IEmployeeManager manager) {
        this.manager = manager;
    }
    public void HireProspect(ProspectiveEmployee e) {
        manager.AddEmployee(e);
    }
}

Когда мы тестируем поведение HiringOfficer HireEmployee, мы заинтересованы в проверке того, что он правильно передал менеджеру сотрудника, чтобы этот перспективный сотрудник был добавлен в качестве сотрудника. Вы часто увидите что-то вроде этого:

// you have an interface IEmployeeManager and a stub class
// called TestableEmployeeManager that implements IEmployeeManager
// that is pre-populated with test data
[Test]
public void HiringOfficerAddsProspectiveEmployeeToDatabase() {
    var manager = new TestableEmployeeManager(); // Arrange
    var officer = new HiringOfficer(manager); // BTW: poor example of real-world DI
    var prospect = CreateProspect();
    Assert.AreEqual(4, manager.EmployeeCount());

    officer.HireProspect(prospect); // Act

    Assert.AreEqual(5, manager.EmployeeCount()); // Assert
    Assert.AreEqual("John", manager.Employees[4].FirstName);
    Assert.AreEqual("Doe", manager.Employees[4].LastName);
    //...
}

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

Но, поведение тестирования часто бывает лучше, и это то, где насмехаются фреймворки:

// using Moq for mocking
[Test]
public void HiringOfficerCommunicatesAdditionOfNewEmployee() {
    var mockEmployeeManager = new Mock<EmployeeManager>(); // Arrange
    var officer = new HiringOfficer(mockEmployeeManager.Object);
    var prospect = CreateProspect();

    officer.HireProspect(prospect); // Act

    mockEmployeeManager.Verify(m => m.AddEmployee(prospect), Times.Once); // Assert
}

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

Можно сделать ваши записи в тестовом классе, но тогда вы эмулируете насмешливые рамки. Если вы собираетесь тестировать поведение - используйте насмешливую структуру.

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

1 - Да, мышление по-прежнему необязательно, но я настоятельно рекомендую его;).

Ответ 2

Инверсия управления - это ваше решение, а не объекты Mock. Вот почему:

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

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

См. Шаблон стратегии

Это приведет вас в цельный, захватывающий мир Inversion of Control. И когда вы вникнете в это, вы обнаружите, что такие проблемы были эффективно решены с помощью контейнеров IoC, таких как AutoFac, Ninject и Structure Map, чтобы назвать несколько.

Стыдные интерфейсы великолепны, и вы можете издеваться над интерфейсом, который затем переходите в IoC. Но вы обнаружите, что IoC является гораздо более надежным решением вашей проблемы. И да, хотя вы можете реализовать только вторую альтернативу только для тестирования, по-прежнему важно сделать это по этой причине - отделить тестируемую стратегию от бизнес-логики EmployeeManager.

Ответ 3

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

По-моему, стоит учиться. Это сэкономит вам много информации. Они также гибки и не всегда требуют интерфейса для генерации тестовой реализации. Например, RhinoMocks могут издеваться над конкретными классами, поскольку они имеют пустой конструктор, а методы - виртуальные. Другим преимуществом является то, что насмешливые API-интерфейсы используют последовательное именование, чтобы вы познакомились с "Mocks", "Stubs" и т.д. В вашем сценарии, кстати, вам нужно заглушка, а не фиктивный. Написание фактического макета вручную может быть более трудоемким, чем использование фреймворка.

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

Это хороший читать по теме рукописных или сгенерированных заглушек

Ответ 4

Создание интерфейса IEmployeeManager для того, чтобы быть в состоянии издеваться, так это то, что большинство разработчиков .NET планировали сделать такой класс testable.

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

Ответ 5

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

Если бы я мог предложить пару книг Head First Design Patterns и Head First Software Development будет намного лучше объяснять понятия, которые я мог бы ответить SO.

Если вы не хотите использовать фальшивую фреймворк, такую ​​как Moq, достаточно просто перевернуть свои собственные макеты/заглушки, вот быстрый блог на нем Прокрутка собственных Mock объектов