使用存根和模拟的正确方法是什么?

发布于 2024-12-03 14:10:00 字数 1394 浏览 0 评论 0原文

这是我的例子:

[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 个模拟,来检查我的操作是否正常工作,我需要检查这 3 个模拟,不同意吗?

我如何以正确的方式进行此测试?

Here's my example:

[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();
}

I have read some tutorials and they say that we should use only one mock per test.

But look at my test, it use 3 mocks, to check if my Action is working the right way I need to check these 3 mocks, do not agree?

How do I make this test in the correct way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

策马西风 2024-12-10 14:10:00

每个单元测试应该只测试一件事。

在单元测试中,您正在测试三个模拟对象。如果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");
}

Each unit test should test only one thing.

In your unit test you are testing three mock objects. If the mockAuthenticationService fails, this will be reported and the unit test will stop there. Any errors with the other Mock objects are not reported and are effectively hidden.

In this situation you should create three unit tests, and in each one verify only one of the Mock objects. The rest are just used as stubs. (A stub is exactly the same as a Mock object, except you dont call VerifyAll on it at the end)

To avoid duplication and wasted effort, you should refactor that unit test so that most of the code is in a separate method. Each of the three unit tests calls this method and then verifies a single Mock.

You also have a test to ensure the correct redirect is called. This should also be in a separate test.

Quite simply:

[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");
}
2024-12-10 14:10:00

简短的回答:“每次测试只有一次模拟。”是模棱两可的。根据需要使用尽可能多的假代码,将被测代码隔离到正在测试一种条件的“单元”。
应该这样表述:每次测试只测试一件事。如果您正在检查多个模拟对象的状态,那么您可能正在测试不止一件事。


长答案:

这里有很多需要回答的问题,以便根据我遇到的最佳实践编写单元测试。

来自(单元测试的艺术)的常用术语,我希望它会变得常见:

Fake - 将被测代码与应用程序其余部分隔离的对象。
存根 - 一个简单的假对象。
Mock - 一个假对象,存储传递给它的内容,您可以检查它来验证测试。
存根和模拟都是假的。

“每次测试仅进行一次模拟。”是错误的。您可以根据需要使用尽可能多的假代码,以将测试中的代码与应用程序的其余部分完全隔离。如果一个方法不带参数,那么就没有什么可以伪造的。如果一个方法采用简单的数据类型,例如intstring,并且没有任何复杂的行为,则不需要伪造它。如果您有 2 个存储库、上下文、传入的服务对象,请伪造所有这些,这样就不会调用其他生产方法。

正如 @Mongus Pong 所说,您应该每个测试有一个条件

测试命名约定:MethodUnderTest_Condition_ExpectedBehaviour 在这种情况下,您不能这样做,因为您测试了多个条件。

测试模式:排列、执行、断言。从您的测试来看,这似乎是您所做的,但您正在安排使用私人成员。您应该在每个测试中将它们替换为变量,因为测试的运行顺序并不总是强制执行,因此无法保证这些变量的状态,从而使您的测试不可靠。

购买一本《单元测试的艺术》http://artofunittesting.com/ 它将回答更多问题解答您的问题,是一项巨大的投资;如果办公室着火我会拿走其中一本书。

Short answer: "only one mock per test." is ambiguous. Use as many fakes as you need to isolate the code under test to a "unit" that is testing one condition.
It should be phrased: Only test one thing per test. If you are checking the state of more than one mock object you are probably testing more than one thing.


Long answer:

There is a lot to answer here to get the unit test written according to the best practices I have come across.

Common terminology from (The Art of Unit Testing), which I hope will come to be common:

Fake - an object that isolates the code under test from the rest of the application.
Stub - a simple fake object.
Mock - a fake object that stores what is passed to it, that you can inspect to verify the test.
Stubs and Mocks are both types of fake.

"only one mock per test." is wrong. You use as many fakes as you need to fully isolate the code under test from the rest of the application. If a method takes no parameters, there's nothing to fake. If a method takes a simple data type e.g. int, string, that doesn't have any complex behaviour, you don't need to fake it. If you have 2 repositories, context, a service object passed in, fake all of them, so no other production methods are being called.

You should have one condition per test as @Mongus Pong has said.

Test naming convention: MethodUnderTest_Condition_ExpectedBehaviour in this case you cannot do that as you have got more than one condition tested.

Test pattern: Arrange, Act, Assert. From your test, it seems as that is what you have done, but you have are arranging using private members. You should replace these with variables in each test, since the running order of tests is not always enforced, the state of these variables cannot be guaranteed, making your tests unreliable.

Buy a copy of "The Art of Unit Testing" http://artofunittesting.com/ it will answer a lot of more of your questions and is a great investment; one of the books that I'd grab if the office caught fire.

記憶穿過時間隧道 2024-12-10 14:10:00

恕我直言,模拟和存根并不是唯一的定义 - 每个作者使用它们都略有不同。

据我了解,当您使用模拟时,存根“模拟”行为或“输出”例如检查模拟对象/接口的“输入”(例如中的验证方法)最小起订量)。

如果你这样看,那么是的,我也认为你应该只使用一个模拟,因为你应该只测试一件事 - 如果你看到它更像是注入可测试接口的存根,那么这是不可能的。

如果这里确实需要VerifyAll,那么您确实使用了3个模拟,但我认为不需要它们。

IMHO mocks and stubs are not that unique defined - every author uses them slightly different.

As I understand stubs "mock" behavior or "output" while you use mocks for example to check "input" into the mocked object/interface (like the Verify-Methods in MOQ).

If you see it this way then yes I too think you should only use one Mock because you should only test one thing - if you see it more like the stubs to inject testable interfaces then it's impossible to do.

If the VerifyAll is really needed here you indeed use 3 mocks, but I don't think they are nedded.

暮倦 2024-12-10 14:10:00

将 Mock 和存根与 Dev Magic Fake 结合使用的最佳方法,以便您可以模拟 UI 和 DB 有关更多信息,请参阅 codePlex 上的 Dev Magic Fake

http://devmagicfake.codeplex.com/

谢谢

M.Radwan

The best way to use Mock and stubs with Dev Magic Fake, so you can mock the UI and the DB for more information see Dev Magic Fake on codePlex

http://devmagicfake.codeplex.com/

Thanks

M.Radwan

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文