Предположим, у нас есть этот интерфейс:
public interface IFileSystem
{
string ReadFile(string filename);
string CombinePaths(string path1, string path2);
}
и следующая конкретная реализация
public class ConcreteFileSystem : IFileSystem
{
public string CombinePaths(string path1, string path2)
{
return path1 + "/" + path2;
}
}
Реализация ReadFile
здесь не важна.
НаличиеЭкземпляр файловой системы очень хорош, поскольку он позволяет имитировать вызовы файловой системы, которые имеют побочные эффекты (например, ReadFile
).
Затем у нас есть тест
var mock = new Mock<IFileSystem>();
var fs = mock.Object;
// How do I forward the calls to Mock<IFileSystem>.CombinePaths to ConcreteFileSystem.CombinePaths?
Assert.IsTrue(SomeClass.SomeMethodThatUsesCombinePaths("specificString1"))
То, что в данный момент выполняетсяв моей базе кода делается такой код в каждом тесте:
mock.Setup(f => f.CombinePaths("specificString1", "specificString2"))
.Returns("specificString1/specificString2");
Но поскольку тесты должны тестировать интерфейс, а не реализацию, это кажется плохим подходом.Кроме того, при дублировании некоторого тестового кода могут появляться незначительные ошибки.
Я думал только о
mock.Setup(f => f.CombinePaths(It.IsAny<string>(), It.IsAny<string>()))
.Returns<string, string>((path1, path2) => new FileSystem().CombinePaths(path1, path2));
(возможно, это можно сократить с помощью некоторого синтаксиса, специфичного для C #).
Такой код в разделе [SetUp]
тестов для каждого метода IFilesystem
больше не зависит от реализации SomeClass.SomeMethodThatUsesCombinePaths
.
У меня вопрос, является ли этохороший подход или как этот подход может быть улучшен.Может быть, есть более фундаментальный способ действовать по-другому.