我们已经进行了相当长时间的TDD,但在重构时仍然存在一些问题。由于我们尽可能地尊重SRP(单一职责原则),因此我们创建了许多组合,供我们的类用于处理常见的职责(例如验证、日志记录等)。
让我们来看一个非常简单的例子:
public class Executioner
{
public ILogger Logger { get; set; }
public void DoSomething()
{
Logger.DoLog("Starting doing something");
Thread.Sleep(1000);
Logger.DoLog("Something was done!");
}
}
public interface ILogger
{
void DoLog(string message);
}
由于我们使用了一个模拟框架,因此我们在这种情况下要进行的测试类型将类似于:
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
loggerMock.Setup(l => l.DoLog("Starting doing something"));
loggerMock.Setup(l => l.DoLog("Something was done!"));
objectUnderTests.Logger = loggerMock.Object;
#endregion
objectUnderTests.DoSomething();
loggerMock.VerifyAll();
}
}
正如您所看到的,测试明显知道我们正在测试的方法实现。我必须承认,这个例子太简单了,但有时我们会有一些涵盖不为测试增加任何价值的职责的组合。
让我们给这个例子增加一些复杂性。
public interface ILogger
{
void DoLog(LoggingMessage message);
}
public interface IMapper
{
TTarget DoMap<TSource, TTarget>(TSource source);
}
public class LoggingMessage
{
public string Message { get; set; }
}
public class Executioner
{
public ILogger Logger { get; set; }
public IMapper Mapper { get; set; }
public void DoSomething()
{
DoLog("Starting doing something");
Thread.Sleep(1000);
DoLog("Something was done!");
}
private void DoLog(string message)
{
var startMessage = Mapper.DoMap<string, LoggingMessage>(message);
Logger.DoLog(startMessage);
}
}
好的,这是一个例子。我会在我的日志记录器实现中包含Mapper相关内容,并在我的接口中保留DoLog(string message)方法,但这只是为了演示我的担忧。
相应的测试结果如下:
[TestClass]
public class ExecutionerTests
{
[TestMethod]
public void Test_DoSomething()
{
var objectUnderTests = new Executioner();
#region Mock setup
var loggerMock = new Mock<ILogger>(MockBehavior.Strict);
var mapperMock = new Mock<IMapper>(MockBehavior.Strict);
var mockedMessage = new LoggingMessage();
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Starting doing something")).Returns(mockedMessage);
mapperMock.Setup(m => m.DoMap<string, LoggingMessage>("Something was done!")).Returns(mockedMessage);
loggerMock.Setup(l => l.DoLog(mockedMessage));
objectUnderTests.Logger = loggerMock.Object;
objectUnderTests.Mapper = mapperMock.Object;
#endregion
objectUnderTests.DoSomething();
mapperMock.VerifyAll();
loggerMock.Verify(l => l.DoLog(mockedMessage), Times.Exactly(2));
loggerMock.VerifyAll();
}
}
哇...想象一下,如果我们使用另一种方式来翻译我们的实体,我将不得不更改每个使用映射器服务的方法的测试。
无论如何,当我们进行重大重构时,我们确实感到一些痛苦,因为我们需要更改一堆测试。
我很想讨论这种问题。我有遗漏什么吗?我们测试的东西太多了吗?