gpt4 book ai didi

unit-testing - 如何正确模拟和单元测试

转载 作者:行者123 更新时间:2023-12-03 10:57:41 24 4
gpt4 key购买 nike

我基本上是在尝试自学如何编码,并且我想遵循良好的做法。单元测试有明显的好处。在单元测试方面也有很多狂热,我更喜欢更实用的编码和生活方法。作为上下文,我目前正在编写我的第一个“真实”应用程序,它是使用 asp.net MVC 的无处不在的博客引擎。我通过自己的调整松散地遵循 MVC Storefront 架构。因此,这是我第一次真正尝试模拟对象。我将把代码示例放在问题的末尾。

我很感激任何可以用来增加我对测试和模拟基础知识的理解的见解或外部资源。我在网上找到的资源通常是针对 mock 的“方式”,我需要更多地了解 mock 的地点、原因和时间。如果这不是问这个问题的最佳地点,请指点我一个更好的地方。

我试图了解我从以下测试中获得的值(value)。 UserService 依赖于 IUserRepository。服务层的值(value)是将逻辑与数据存储分开,但在这种情况下,大多数 UserService 调用只是直接传递给 IUserRepository。没有太多实际逻辑可以测试的事实也可能是我担心的根源。我有以下顾虑。

  • 感觉就像代码只是在测试模拟框架是否正常工作。
  • 为了模拟依赖关系,它使我的测试对 IUserRepository 实现有太多的了解。这是必要的邪恶吗?
  • 我实际上从这些测试中获得了什么值(value)?被测服务的简单性是否让我怀疑这些测试的值(value)。

  • 我正在使用 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 = "joe@joeblow.com",
    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");
    }

    最佳答案

    本质上,您在这里测试的是方法被调用,而不是它们是否真的有效。这是模拟应该做的。他们不调用该方法,而是检查该方法是否被调用,并返回 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)))

    现在这有一些值(value),因为您正在测试一些实际行为。当然,您也可以直接通过传入“true”或“false”来测试它。您甚至不需要模拟该测试。测试期望很好,但是我已经编写了很多这样的测试,并且得出了与您正在达到的相同的结论——它只是没有那么有用。

    所以简而言之,当您想要抽象出外部性(如数据库或 Web 服务调用等)并在此时注入(inject)已知值时,模拟很有用。但是直接测试模拟并不经常有用。

    关于unit-testing - 如何正确模拟和单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/476540/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com