单元测试 - 是否应该包装/模拟所有库依赖项



我正在编写一个将读取/写入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 对象。这是一个依赖项,但我不确定它是否应该被包装/嘲笑并注入到类构造函数中,或者只是保持原样。如果我将其包装成接口并发现我需要使用类中的更多方法,则编辑接口本身可能会变得耗时。

这是一个"取决于"的方案。嘲笑/包装每个依赖项可能是矫枉过正的,但这太宽泛了,无法在这个问题中涵盖。

对于这种特殊情况,在我看来,根据我的经验,序列化程序代码不需要抽象。如果您决定使用其他方法,则可以只编辑该方法,而不会影响类的依赖项。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、路径等。

最新更新