Еще один замечательный ресурс - книга Джерарда Месароса "Тестовые шаблоны xUnit: рефакторинг тестового кода"

сновном пытаюсь научиться кодировать, и я хочу следовать хорошей практике. Есть очевидные преимущества для модульного тестирования. Когда речь заходит о юнит-тестировании, здесь также много фанатизма, и я предпочитаю гораздо более прагматичный подход к кодированию и жизни в целом. В настоящее время я пишу свое первое «настоящее» приложение, которое представляет собой вездесущий движок блогов, использующий asp.net MVC. Я слабо слежу за архитектурой MVC Storefront со своими собственными настройками. Таким образом, это мой первый настоящий набег на насмешливые объекты. Я приведу пример кода в конце вопроса.

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

Я пытаюсь понять ценность, которую я получаю из следующих тестов. UserService зависит от IUserRepository. Значение сервисного уровня состоит в том, чтобы отделить вашу логику от хранилища данных, но в этом случае большинство вызовов UserService просто передаются прямо в IUserRepository. Тот факт, что нет реальной логики для тестирования, может быть источником моих опасений. У меня есть следующие проблемы.

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

Я использую NUnit и Rhino.Mocks, но должно быть достаточно очевидно, что я пытаюсь достичь.

    [SetUp]
    public void Setup()
    {
        userRepo = MockRepository.GenerateMock<IUserRepository>();
        userSvc = new UserService(userRepo);
        theUser = new User
        {
            ID = null,
            UserName = "http://joe.myopenid.com",
            EmailAddress = "[email protected]",
            DisplayName = "Joe Blow",
            Website = "http://joeblow.com"
        };
    }

    [Test]
    public void UserService_can_create_a_new_user()
    {
        // Arrange
        userRepo.Expect(repo => repo.CreateUser(theUser)).Return(true);

        // Act
        bool result = userSvc.CreateUser(theUser);

        // Assert
        userRepo.VerifyAllExpectations();
        Assert.That(result, Is.True, 
          "UserService.CreateUser(user) failed when it should have succeeded");
    }

    [Test]
    public void UserService_can_not_create_an_existing_user()
    {
        // Arrange
        userRepo.Stub(repo => repo.IsExistingUser(theUser)).Return(true);
        userRepo.Expect(repo => repo.CreateUser(theUser)).Return(false);
        // Act
        bool result = userSvc.CreateUser(theUser);

        // Assert
        userRepo.VerifyAllExpectations();
        Assert.That(result, Is.False, 
            "UserService.CreateUser() allowed multiple copies of same user to be created");
    }

Ответы на вопрос(1)

Решение Вопроса

вы тестируете здесь то, что методы вызываются, а не работают ли они на самом деле. Это то, что издевательства должны делать. Вместо вызова метода, они просто проверяют, был ли вызван метод, и возвращают все, что есть в выражении Return (). Итак, в вашем утверждении здесь:

Assert.That(result, Is.False, "error message here");

Это утверждение ВСЕГДА будет успешным, потому что ваше ожидание ВСЕГДА вернет false, потому что оператор Return:

userRepo.Expect(repo => repo.CreateUser(theUser)).Return(false);

Я предполагаю, что это не так полезно в этом случае.

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

public string displayMessage(bool userWasCreated) {
    if (userWasCreated)
        return "User created successfully!";
    return "User already exists";
}

тогда ваш тест будет

userRepo.Expect(repo => repo.CreateUser(theUser)).Return(false);
Assert.AreEqual("User already exists", displayMessage(userSvc.CreateUser(theUser)))

Теперь это имеет определенную ценность, потому что вы тестируете некоторое реальное поведение. Конечно, вы также можете просто проверить это напрямую, передав «true» или «false». Вам даже не нужно издеваться над этим тестом. Ожидания по тестированию - это хорошо, но я написал множество подобных тестов и пришел к тому же выводу, к которому вы пришли - он просто не так полезен.

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

 Ben Robbins25 янв. 2009 г., 19:39
Гораздо больше смысла проверять поведение. Я не мог понять ценность написания тестов, которые будут автоматически проходить из-за того, как я настроил макеты. Поведение при тестировании фактически дает представление о функции тестируемого кода.

Ваш ответ на вопрос