我正在写一个工具 - 建立与Sql的连接并运行一系列存储过程 - 命中文件系统以验证并删除文件 - 通过公开的API与其他子系统进行交流
我对TDD的概念不熟悉,但一直在做很多阅读。我想在这个开发中应用TDD,但我被卡住了。与外部系统有很多交互需要被模拟/存根或伪造。我发现困难的是在TDD中采用这种方法的正确方法。这里是我想要完成的一个示例。
public class MyConfigurator
{
public static void Start()
{
CheckSystemIsLicenced(); // will throw if its not licenced. Makes call to a library owned by company
CleanUpFiles(); // clean up several directories
CheckConnectionToSql(); //ensure connection to sql can be made
ConfigureSystemToolsOnDatabase(); //runs a set of stored procedure. Range of checks are also implemented and will throw if something goes wrong.
}
}
在此之后,我有另一个课程,如果出现问题,我们会清理系统。出于这个问题的目的,它不是那么相关,但它本质上只是清除某些表并修复数据库,以便该工具可以从头开始重新运行以完成其配置任务。
在使用TDD时,我似乎最终只有这样的测试(假设我使用的是FakeItEasy)
A.CallTo(()=>fakeLicenceChecker.CheckSystemIsLicenced("lickey")).MustHaveHappened();
这只是一大堆测试,似乎是#34; MustHaveHappened"。难道我做错了什么?有没有不同的方法来使用TDD启动这个项目?或者这是一个特殊情况,也许不推荐TDD?任何指导都将不胜感激。
答案 0 :(得分:1)
在这个特定方法中,您唯一可以测试的是调用方法。通过断言模拟类来做你正在做的事情是可以的。由您决定此特定测试是否有价值取决于您。 TDD假设测试一切,但我发现将测试集中在增加价值的场景上更为实际。其他人很难做出这样的决定,但你应该相信自己能够在每个特定情况下进行调用。
答案 1 :(得分:1)
我认为集成测试会增加最大的收益。使用真正的数据库和文件系统。
如果工具中有复杂的逻辑,那么您可能需要重新构建工具设计以抽象出DB和fileSystem并使用模拟编写单元测试。从你发布的代码片段来看,它看起来像是一个简单的脚本。
答案 2 :(得分:1)
在您的示例中,如果单元测试的排列显示lickey
作为输入,则断言已使用适当的值调用端点是合理的。在更复杂的场景中,输入到断言流程涵盖了更多子系统,因此测试本身看起来并不简单。您可以将ID
值设置为输入,并测试您输出的值是否与输入ID
确定性相关的对象的值。
TDD的一个方面是代码在测试时没有变化 - 除了功能等同的重构。因此,您的第一个测试自然会在最外端点排列和断言数据。您将从一个将实际文件写入文件系统的测试开始,调用您的代码,然后检查该文件是否按预期删除。当然,文件系统对于便携式测试来说是一个混乱的工作空间,因此您可能会尽早决定将文件系统抽象一步。通过使用EF和模拟DbContext或使用模拟的存储库模式与数据库同上。这些抽象可以是TDD前应用程序架构决策。
我经常做的事情是使用以IFileSystem
接口开头的实用程序代码,该接口声明模仿System.IO.File中许多可用内容的方法。在生产中,我使用IFileSystem
的实现,它只是传递给File.XXX()
方法。然后,您可以模拟并验证界面,而不是尝试设置和清理真实文件。