Как правильно использовать заглушки и макеты? - PullRequest
11 голосов
/ 06 сентября 2011

Вот мой пример:

[TestMethod]
public void NewAction_should_return_IndexAction()
{
    NewViewModel viewModel = new NewViewModel()
    {
        Name = "José Inácio Santos Silva",
        Email = "joseinacio@joseinacio.com",
        Username = "joseinacio"
    };

    //IsUserRegistered is used to validate Username, Username is unique.
    _mockAuthenticationService.Setup(x => x.IsUserRegistered(viewModel.Username )).Returns(false);

    //IsUserRegistered is used to validate Email, Email is unique.
    _mockUsuarioRepository.Setup(x => x.GetUserByEmail(viewModel.Email));
    _mockDbContext.Setup(x => x.SaveChanges());
    _mockUsuarioRepository.Setup(x => x.Add(It.IsAny<User>()));

    _userController = new UserController(_mockUsuarioRepository.Object, _mockDbContext.Object, _mockAuthenticationService.Object);

    ActionResult result = _userController.New(viewModel);

    result.AssertActionRedirect().ToAction("Index");

    _mockAuthenticationService.VerifyAll();
    _mockUsuarioRepository.VerifyAll();
    _mockDbContext.VerifyAll();
}

Я прочитал несколько уроков, и они говорят, что мы должны использовать только один макет на тест .

Но посмотрите на мой тест, он использует 3 макета , чтобы проверить, правильно ли работает Действие . Мне нужно проверить эти 3 макета, не согласны?

Как правильно выполнить этот тест?

Ответы [ 4 ]

4 голосов
/ 06 сентября 2011

Каждый модульный тест должен проверять только одну вещь.

В своем модульном тесте вы тестируете три фиктивных объекта.Если mockAuthenticationService завершится ошибкой, об этом будет сообщено, и модульное тестирование на этом остановится.О любых ошибках с другими объектами Mock не сообщается, и они эффективно скрыты.

В этой ситуации вы должны создать три модульных теста, и в каждом из них проверить только один из объектов Mock.Остальные просто используются как заглушки. (Заглушка точно такая же, как и в случае с Mock-объектом, за исключением того, что вы не вызываете VerifyAll для него в конце)большая часть кода находится в отдельном методе.Каждый из трех модульных тестов вызывает этот метод, а затем проверяет один Mock.

У вас также есть тест для проверки правильности перенаправления.Это также должно быть в отдельном тесте.

Проще говоря:

[TestMethod]
public void NewAction_should_checkUserRegistered()
{
    SetupTest();
    _mockAuthenticationService.VerifyAll();
}

[TestMethod]
public void NewAction_should_GetUserByEmail()
{
    SetupTest();
    _mockUsuarioRepository.VerifyAll();
}

[TestMethod]
public void NewAction_should_SaveDBContext()
{
    SetupTest();
    _mockDbContext.VerifyAll();
}

[TestMethod]
public void NewAction_should_return_Redirects_Action()
{
    var novoActionResult = SetupTest();
    novoActionResult.AssertActionRedirect().ToAction("Index");
}
2 голосов
/ 06 сентября 2011

Краткий ответ: «только один макет за тест». неоднозначно. Используйте столько фальшивок, сколько вам нужно, чтобы изолировать тестируемый код от «модуля», который тестирует одно условие. Следует сформулировать: Тестируйте только одну вещь за тест. Если вы проверяете состояние более одного фиктивного объекта, вы, вероятно, тестируете более одной вещи.


Длинный ответ:

Здесь есть много ответов, чтобы написать модульный тест в соответствии с лучшими практиками, с которыми я сталкивался.

Общая терминология из (Искусство модульного тестирования), которая, я надеюсь, станет общей:

Fake - объект, который изолирует тестируемый код от остальной части приложения.
Заглушка - простой поддельный объект.
Mock - фальшивый объект, который хранит то, что ему передано, и который можно проверить для проверки теста.
Окурки и издевательства - оба типа подделки.

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

Вы должны иметь одно условие на тест , как сказал @Mongus Pong.

Соглашение о тестировании имен: MethodUnderTest_Condition_ExpectedBehaviour, в этом случае вы не можете этого сделать, поскольку вы прошли более одного испытания условия.

Тестовый образец: Arrange, Act, Assert . Из вашего теста кажется, что это то, что вы сделали, но вы организуете с использованием частных членов. Их следует заменять переменными в каждом тесте, поскольку порядок выполнения тестов не всегда соблюдается, состояние этих переменных не может быть гарантировано, что делает ваши тесты ненадежными.

Купите копию "Искусство юнит-тестирования" http://artofunittesting.com/, она ответит на многие ваши вопросы и будет отличным вложением; одна из книг, которую я бы взял, если загорелся офис.

1 голос
/ 06 сентября 2011

ИМХО издевательства и заглушки не так однозначно определены - каждый автор использует их немного по-разному.

Как я понимаю заглушки поведение "mock" или "output", когда вы используете mocks , например, для проверки "ввода" в макет объект / интерфейс (например, Verify-Methods в MOQ).

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

Если здесь действительно нужен VerifyAll, вы действительно используете 3 насмешки, но я не думаю, что они являются встроенными.

0 голосов
/ 06 сентября 2011

Лучший способ использовать Mock и заглушки с Dev Magic Fake, так что вы можете смоделировать UI и DB для получения дополнительной информации, см. Dev Magic Fake на codePlex

http://devmagicfake.codeplex.com/

Спасибо

М.Радван

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...