单元测试和TDD的新手,我写了比传统LOB更多的OS实用程序。所以,我理解你如何模拟或存根数据库或文件,但是如下:
答案 0 :(得分:5)
您无法使用单元测试来测试服务是否实际停止。这就是其他测试(例如集成测试)的用途。通常,值得测试的是,您的被测系统(SUT)是否实际上正在进行正确的呼叫以停止服务。
在这种情况下,可以将这些具体的API包装到专门针对消费者需求的界面(façade)中。然后模拟该界面并声明您正在测试的消费者使用正确的参数调用了适当的成员。
示例(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();
}
}
As Matt already pointed out在某些环境中,这些工具允许您替换具体实现而无需引入抽象(此处:IServiceController
)。我倾向于尽可能地避免这些,因为它们通常不仅使用起来有点乏味,而且似乎也强制执行糟糕的设计选择(例如,取决于具体实现而不是抽象)。我将Microsoft Moles或TypeMock Isolator等工具更多地视为可以帮助您处理遗留代码库的工具,这些代码库已经有大量糟糕的设计选择,您无法立即摆脱(立即)。
答案 1 :(得分:2)
对于某些代码,可能无法以松散耦合的方式使用存根或mocks 直接进行单元测试*,因为您依赖的代码可能不会松散耦合。您可能需要最终编写集成测试。
如果是这种情况,那么写一个包装类(即使用adapter或facade模式)可能会优于非单元可测试代码由集成测试驱动。这样,当您需要(例如,启动/停止Windows服务)时,您仍然可以使用单元测试在代码中的其他位置存根/模拟您的包装器类。您的包装器类也是测试驱动的,但只是使用集成测试。
* 如果在.NET上有一些工具可能允许你仍然编写单元测试并注入某种虚假对象,例如Microsoft Moles(很快将命名为Fakes)或TypeMock Isolator,但这些情况下,测试会更慢,需要更多的代码来编写。虽然我使用过Moles,但我个人宁愿包装难以测试的代码而不是使用这些工具。