单元测试和 TDD 的新手,我编写的操作系统实用程序比传统 LOB 多得多。因此,我了解您如何模拟或存根数据库或文件,但是如何处理以下内容:
- 针对 Active Directory 的身份验证
- 停止 NT 服务
- 针对 AD 组的授权
- ETC...
单元测试和 TDD 的新手,我编写的操作系统实用程序比传统 LOB 多得多。因此,我了解您如何模拟或存根数据库或文件,但是如何处理以下内容:
您无法使用单元测试测试服务是否实际停止。这就是其他测试(例如集成测试)的用途。但是,通常值得测试的是,您的被测系统 (SUT) 是否实际上是在进行正确的调用以停止服务。
在这种情况下,可以将这些具体的 API 包装到专门针对您的消费者需求量身定制的界面(外观)中。然后模拟该接口并断言您正在测试的消费者已使用正确的参数调用了适当的成员。
示例(c#、NUnit、FakeItEasy):
// implementation of this interface basically wraps the concrete service APIs of the OS
public interface IServiceController
{
public void Start(string serviceName);
public void Stop(string serviceName);
}
// consumer that wants to stop a specific service and that is your SUT
public class SomeConsumer
{
// ctor takes dependency on a service controller
SomeConsumer(IServiceController controller)
{
// ...
}
public void DoSomethingThatRequiresAServiceStop()
{
// ...
}
}
[TestFixture]
public class SomeConsumerTests
{
[Test]
public void DoSomethingThatRequiresAServiceStop_StopsServiceXYZ()
{
// arrange
IServiceController mockServiceConntroler = A.Fake<IServiceController>();
SomeConsumer sut = new SomeConsumer(mockServiceController);
// act
sut.DoSomethingThatRequiresAServiceStop();
// assert
A.CallTo(() => mockServiceConntroler.Stop("XYZ")).MustHaveHappend();
}
}
正如马特已经在某些环境中指出的那样,这些工具允许您替换具体实现而无需引入抽象(此处:)IServiceController
。我倾向于尽可能避免这些,因为它们通常不仅使用起来有点乏味,而且似乎会强制选择错误的设计(例如,取决于具体的实现而不是抽象)。我认为 Microsoft Moles 或 TypeMock Isolator 之类的工具更多地是帮助您处理遗留代码库的工具,这些代码库已经有大量您无法摆脱的糟糕设计选择(立即)。
对于某些代码,可能无法直接以松散耦合的方式使用存根或模拟进行单元测试*,因为您依赖的代码本身可能不是松散耦合的。您可能最终需要编写集成测试。
如果是这种情况,最好在集成测试驱动的不可单元测试的代码上编写一个包装类(即使用适配器或外观模式)。这样,当您需要启动/停止 Windows 服务时,您仍然可以使用单元测试在代码的其他地方存根/模拟您的包装类。您的包装类也是测试驱动的,但只是使用集成测试。
*如果在 .NET 上,一些工具可能仍然允许您编写单元测试并注入某种假对象,例如 Microsoft Moles(很快将被命名为 Fakes)或 TypeMock Isolator 在这些情况下,但测试会更慢并且写更多的代码。虽然我使用过 Moles,但我个人更愿意封装难以测试的代码,而不是使用这些工具。