使用两个功能进行单元测试

Chr*_*s W 7 c# nunit unit-testing

我已经忽略了一段时间的单元测试.我写了单元测试,但他们相当差.我现在正在阅读"单元测试的艺术",以使自己达到最佳状态.

如果我有一个界面,如:

public interface INotificationService
{
    void AddError(string _error);
    void AddIssue(string _issue);

    IEnumerable<string> FetchErrors();
    IEnumerable<string> FetchIssues();
}
Run Code Online (Sandbox Code Playgroud)

此接口的具体实现包含:

    private readonly ICollection<Message> messages;
Run Code Online (Sandbox Code Playgroud)

添加错误或问题会创建一个带有枚举的新消息,表示其类型并将其添加到集合中.调用FetchErrors()/ FetchIssues()从集合中返回该类型的消息.

以下测试是否有效?:

    [Test]
    public void FetchErrors_LoggingEnabledAddErrorFetchErrors_ReturnsError()
    {
        notificationService = new NotificationService();
        notificationService.AddError("A new error");

        Assert.AreEqual(new []{"A new error"}, notificationService.FetchErrors());
    }
Run Code Online (Sandbox Code Playgroud)

我担心的是我首先调用AddError(),然后测试FetchErrors()的结果.所以我称之为两个功能.这是不正确的?

我应该将集合公开并直接断言它包含包含已记录错误消息的相应类型的消息吗?

在这种情况下,最佳做法是什么?

Stu*_*tLC 3

您的方法看起来不错 - 通过公共方法测试实现是设计中唯一可用的访问。

为了相互独立地测试这些方法,您需要使用像反射这样的白盒测试后门来访问私有消息,以确保各个Add*方法有效。这根本不是一个好主意,因为每次底层 CUT 实现发生更改时,您的测试都会中断(在运行时)。

反射的一个常见的、更好的替代方法是在被测试的具体类上公开一个非接口internal方法,它允许您对实现字段进行某种“维护舱口”访问ICollection<Message> messages;,然后允许InternalsVisibleTo访问单元测试程序集。

需要注意的是 - 您会发现Assert.AreEqual在字符串上可枚举将进行引用比较并因此失败(因为您正在将其与新数组进行比较)。

您可能需要将其更改为类似以下内容:

Assert.IsTrue(notificationService.FetchErrors().Contains("A new error"));
Run Code Online (Sandbox Code Playgroud)

编辑

在我看来,你不需要public在 .Net 中创建该字段。由于您已经有了接口抽象,理论上应该限制访问 CUT 的方式,因此使用internal范围并InternalsVisibleTo允许 UT 特殊访问是很常见的。Jon Skeet 等人在这里提到过这一点

#region Unit Testing side-door
internal ICollection<Message> Messages
{
   get { return _messages };
   // No Setter
}
#endregion
Run Code Online (Sandbox Code Playgroud)