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

Каков правильный способ использования Stubs и Mocks?

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

[TestMethod]
public void NewAction_should_return_IndexAction()
{
    NewViewModel viewModel = new NewViewModel()
    {
        Name = "José Inácio Santos Silva",
        Email = "[email protected]",
        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 mocks, чтобы проверить, работает ли мое действие правильно, мне нужно проверить эти 3 mocks, не согласны?

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

4b9b3361

Ответ 1

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

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

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

Чтобы избежать дублирования и потраченного впустую усилия, вы должны реорганизовать этот unit test, чтобы большая часть кода была в отдельном методе. Каждый из трех модульных тестов вызывает этот метод, а затем проверяет один макет.

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

Довольно просто:

[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

Короткий ответ: "только один макет за тест". является неоднозначным. Используйте столько подделок, сколько вам нужно, чтобы изолировать тестируемый код до "единицы", которая тестирует одно условие. Он должен быть сформулирован: Проверять только одну вещь на тест. Если вы проверяете состояние более чем одного макетного объекта, вы, вероятно, испытываете более чем одну вещь.


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

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

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

Fake - объект, который изолирует тестируемый код от остальной части приложения.
Stub - простой поддельный объект.
Mock - поддельный объект, который хранит то, что ему передается, которое вы можете проверить, чтобы проверить тест.
Stubs и Mocks - оба типа подделок.

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

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

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

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

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

Ответ 3

IMHO mocks и stubs не уникальны, поскольку каждый автор использует их несколько иначе.

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

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

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

Ответ 4

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

http://devmagicfake.codeplex.com/

Спасибо

M.Radwan