【问题标题】:Unit Testing - Should all library dependencies be wrapped/mocked单元测试 - 是否应该包装/模拟所有库依赖项
【发布时间】:2017-08-25 20:40:11
【问题描述】:

我正在编写一个将读取/写入 Json 文件 (C#) 的类。

我已经在模拟文件系统以进行单元测试,现在我想知道是否还应该包装和模拟在创建时传递给类的序列化程序对象。

我知道必须模拟文件系统,否则我将不得不为测试创建文件,这将使它们成为集成测试。

我是否也应该将序列化程序包装在一个接口中并模拟它?如果是这样,我是否还应该包装/模拟我编写的其他类中的每个依赖项?如果我使用来自外部类的许多方法,将它们包装在接口中似乎是个大问题。

编辑:

public class Sources
{
    private readonly IDirectory _directory;
    private readonly IFile _file;

    public Sources(IDirectory directory, IFile file)
    {
        _directory = directory;
        _file = file;
    }

    public LibrarySource GetSource(string filePath)
    {
        using (var streamReader = _file.OpenText(filePath))
        using (var jsonTextReader = new JsonTextReader(streamReader.StreamReaderInstance))
        {
            JsonSerializer serializer = new JsonSerializer();
            var sourceDto = serializer.Deserialize<LibrarySourceDto>(jsonTextReader);
            return SourceMapper.Map(sourceDto);
        }
    }
}

在这段代码中,我在类中创建 JsonSerializer 对象。这是一个依赖项,但我不确定它是否应该被包装/模拟并注入到类构造函数中,还是保持原样。如果我将它包装在和接口中,发现我需要使用类中的更多方法,那么编辑接口本身可能会变得很耗时。

【问题讨论】:

  • 这是一个“视情况而定”的场景。模拟/包装每个依赖项可能有点矫枉过正,但这太宽泛了,无法在这个问题中涵盖。尝试通过minimal reproducible example 缩小您的问题范围,看看社区是否可以帮助您。
  • 在我看来,根据我的经验,序列化程序代码不需要抽象。如果您决定使用不同的方法,您可以只编辑该方法而不影响类的依赖项。也就是说,我也遇到过将IJsonSerializer 作为依赖项的情况。但同样,这完全取决于您的选择。

标签: c# unit-testing mocking


【解决方案1】:

这是一个“视情况而定”的场景。模拟/包装每个依赖项可能有点矫枉过正,但这太宽泛了,无法在这个问题中涵盖。

在我看来,根据我的经验,对于这种特殊情况,不需要对序列化程序代码进行抽象。如果您决定使用不同的方法,您可以只编辑该方法而不影响类的依赖项。 Sources 类执行提供源代码的单一职责,无论它是如何实现的。这才是最重要的。

也就是说,我也遇到过将IJsonSerializer 作为依赖项的情况。但同样,这完全取决于您自己的选择。

/// <summary>
/// Provides JSON Serialize and Deserialize.
/// </summary>
public interface IJsonSerializer : ISerializer {

}

/// <summary>
/// Serialization interface that supports serialize and deserialize methods
/// </summary>
public interface ISerializer {
    /// <summary>
    /// Serialize the specified object into a string
    /// </summary>
    /// <param name="obj">object to serialize</param>
    string Serialize(object obj);
    /// <summary>
    /// Deserialize a string into a typed object
    /// </summary>
    /// <typeparam name="T">type of object</typeparam>
    /// <param name="input">input string</param>
    T Deserialize<T>(string input);
}

实现包装了用于项目的 JSON API。

例如

public class Sources {
    private readonly IDirectory _directory;
    private readonly IFile _file;
    private readonly IJsonSerializer serializer; 

    public Sources(IDirectory directory, IFile file, IJsonSerializer serializer) {
        _directory = directory;
        _file = file;
        this.serializer = serializer;
    }

    public LibrarySource GetSource(string filePath) {
        var sourceDto = serializer.Deserialize<LibrarySourceDto>(_file.ReadAllText(filePath));
        return SourceMapper.Map(sourceDto);
    }
}

序列化程序的实现取决于您。它可以采用原始 json、路径等。

【讨论】:

    猜你喜欢
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 2016-02-19
    • 2013-07-07
    • 1970-01-01
    相关资源
    最近更新 更多