c# - 滥用最小起订量来测试 if 条件

标签 c# unit-testing moq

考虑以下类(class):

public class test
{
    public void start()
    {
        if (true)
            called();
    }

    internal protected virtual void called()
    {

    }
}

我想对 if (true) 进行测试。我最初的想法是使用 Moq 来验证是否调用了 called()。我结束了这个测试:

[TestFixture]
public partial class TestMethodInvocation
{
    [Test]
    public void TestWithMoqVerify()
    {
        var mock = new Mock<test>() {CallBase = true};
        mock.Object.start();
        mock.Verify(t => t.called());
    }
}

我在让它工作时遇到了一些麻烦并发布了 this question我被建议不要使用 Moq 来模拟被测类。

所以我添加了一个子类,并使用一个属性来测试是否调用了该方法:

public class test2 : test
{
    public bool WasCalled { get; set; }

    internal protected override void called()
    {
        WasCalled = true;
    }
}

public partial class TestMethodInvocation
{
    [Test]
    public void TestWithSubclassProperty()
    {
        var test = new test2();
        test.start();
        Assert.IsTrue(test.WasCalled);
    }
}

这两种方法都有效,但 Moq 实现实际上是测试代码量的一半,因为我不需要创建子类。像这样使用 Moq 是不是很糟糕,或者我应该使用另一个框架来做这种测试?或者这是我的代码设计中出现问题的结果?

最佳答案

是否应该为调用验证编写测试存在一些争论,我试图避开它们,我宁愿测试外部行为。您可以测试一些东西,看看是否达到了预期的结果,而无需深入研究内部结构。这当然并不总是可能的。

现在,话虽如此,我将尝试给您举个例子(尽我所能)。假设我们有一个名为 Greeter 的类 - 它应该向所有 stackoverflow 订阅者发送烦人的 SMS。现在,假设为了发送 SMS,您在其他地方编写了一些其他基础设施代码(已经过测试)。假设此代码将是一个名为 IMessageService 的接口(interface)的实现(如果我的示例很糟糕,请见谅):

public interface IMessageService
{
    void SendSMS(string message);
}

此外,假设您有一个 SubscriberRepository,它将为您提供所有 StackOverflow 订阅者。像这样的东西:

public interface ISubscriberRepository
{
    IEnumerable<Subscriber> GetStackOverflowSubscribers();
}

这是您的 Greeter 类:

public class Greeter
{
    private readonly IMessageService _messageService;
    private readonly ISubscriberRepository _subscriberRepository;

    public Greeter(IMessageService messageService, ISubscriberRepository subscriberRepository)
    {
        _messageService = messageService;
        _subscriberRepository = subscriberRepository;
    }

    public void SendGreetingToStackOverflow()
    {
        IEnumerable<Subscriber> stackOverflowers = _subscriberRepository.GetStackOverflowSubscribers();

        foreach (Subscriber overflower in stackOverflowers)
        {
            _messageService.SendSMS("Hello World!");
        }
    }
}

您会看到它实际上是在使用 IMessageService 发送短信。此时您想要(可能)测试 SendSMS() 是否被调用了 x 次。在这种情况下,次数应该与 StackOverflow 订阅者的数量相同。所以你的测试看起来像这样:

[Test]
public void SendGreetingToStackOverflow_CallsIMessageServiceSendSMSTwoTimes()
{
    var mockMessageService = new Mock<IMessageService>();
    var mockSubscriberRepo = new Mock<ISubscriberRepository>();

    // we will mock the repo and pretend that it returns 2 subscibers
    mockSubscriberRepo
        .Setup(x => x.GetStackOverflowSubscribers())
        .Returns(new List<Subscriber>() {new Subscriber(), new Subscriber()});

    // this is the one we're testing, all dependencies are fake
    var greeter = new Greeter(mockMessageService.Object, mockSubscriberRepo.Object);

    greeter.SendGreetingToStackOverflow();

    // was it called 2 times (for each subscriber) ?
    mockMessageService.Verify(
        x => x.SendSMS("Hello World!"),
        Times.Exactly(2));
}

再次抱歉,这可能不是最好的例子,但这是漫长的一天,这是我能想到的最好的例子:)。

希望对你有帮助。

关于c# - 滥用最小起订量来测试 if 条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18428652/

相关文章:

c# - 文本更改事件未触发

c# - Listview 虚拟模式无法动态添加项目

c++ - 使用 Qt : change StandardPaths used in tested functions 进行单元测试

c# - 不单独运行测试时,Moq.Proxy.CaSTLeProxyFactory 的类型初始值设定项抛出的异常

javascript - ASP NET 从 javascript 读取 session

c# - 如何在c#中实现一个循序渐进的按钮?

unit-testing - "Normal"测试失败时代码库与测试断言问题的比率?

c# - 如何更改被 mock 的对象?

c# - 如何在 C# 中最小化 protected 属性

c# - 从部分模拟对象返回模拟对象不起作用