C# NET中的单元测试服务层
我正在开发一个新的MVC5应用程序。我的项目中有一个集成层,其中有一个对外部web服务的服务引用 然后,我创建了一个包含一些方法的接口,然后我创建了一个服务类,如下所示:C# NET中的单元测试服务层,c#,unit-testing,design-patterns,moq,C#,Unit Testing,Design Patterns,Moq,我正在开发一个新的MVC5应用程序。我的项目中有一个集成层,其中有一个对外部web服务的服务引用 然后,我创建了一个包含一些方法的接口,然后我创建了一个服务类,如下所示: public class MyService : IMyService { private readonly ExternalServiceClient _serviceClient; public MyService() { _serviceClient = new External
public class MyService : IMyService
{
private readonly ExternalServiceClient _serviceClient;
public MyService()
{
_serviceClient = new ExternalServiceClient ("WSHttpBinding_IMyService");
}
public string GetName(Coordinate[] coordinates)
{
string name = string.Empty;
name = _serviceClient.GetInformationsForCoordinates(coordinates);
return name;
}
注意,这是精简的,实际上会添加带有异常处理的try-catch块,等等
我有一个用于集成层单元测试的Integration.Tests项目。测试GetName方法的最佳实践方法是什么。我是否应该在测试项目中向服务的端点添加另一个服务引用,或者如果我们正在使用Moq,我是否可以创建一个Web层将调用的实际服务的实例,以及如何做到这一点?适合您情况的最佳实践在于您的体系结构设计 您的服务显然依赖于
ExternalServiceClient
,并且您将其保存在MyService
类中,这不允许您轻松切换依赖关系,在测试时会让您头疼
真正的问题应该是:
如何设计我的服务以使其易于测试
答案是
因为您将能够模拟MyService
依赖关系,所以您将能够对其进行彻底的测试,并能够通过红绿重构来证明您的服务能够完美地工作
依我拙见,你们班应该这样:
public class MyService : IMyService {
public MyService(ExternalServiceClient serviceClient) {
externalServiceClient = serviceclient;
}
public string GetName(Coordinate[] coordinates) {
string name = string.Empty;
name = externalServiceClient.GetInformationForCoordinates(coordinates);
return name;
}
private readonly ExternalServiceClient externalServiceclient;
}
通过这种方式,您将能够根据自己的意愿替换依赖项,因此可以使用mock
使用和Moq
,您可以按如下方式测试您的服务:
[TestFixture]
public class MyServiceTests {
[TestFixture]
public class GetCoordinates : MyServiceTests {
// Given
string expected = "Name";
Coordinate[] coordinates = new Coordinate[] { ... }
externalServiceClientMock.Setup(esc => esc.GetInformationForCoordinates(coordinates)).Returns(expected);
// When
string actual = myService.GetName(coordinates);
// Then
externalServiceClientMock.Verify(esc => esc.GetInformationCoordinates(coordinates));
Assert.AreEqual(expected, actual);
}
[SetUp]
public void MyServiceSetup() {
externalServiceClientMock = new Mock<ExternalServiceClient>("WSHttpBinding_IMyService");
myService = new MyService(externalServiceClientMock.Object);
}
private Mock<ExternalServiceClient> externalServiceClientMock;
private MyService myService;
}
[TestFixture]
公共类MyServiceTests{
[测试夹具]
公共类GetCoordinates:MyServiceTests{
//给定
字符串应为=“名称”;
坐标[]坐标=新坐标[]{…}
externalServiceClientMock.Setup(esc=>esc.GetInformationForCoordinates(坐标)).Returns(预期);
//什么时候
string actual=myService.GetName(坐标);
//然后
externalServiceClientMock.Verify(esc=>esc.GetInformationCoordinates(坐标));
断言.AreEqual(预期、实际);
}
[设置]
public void myservices设置(){
externalServiceClientMock=新Mock(“WSHttpBinding_IMyService”);
myService=newmyservice(externalServiceClientMock.Object);
}
私有模拟externalServiceClientMock;
私人MyService-MyService;
}
一般来说,使用参数化构造函数能够注入对对象构造的依赖关系是一种很好的做法(因为您已经为ExternalServiceClient
提供了只读语义)。然后您可以在测试用例中注入模拟
在您的情况下,如果存在在ExternalServiceClient
private readonly ExternalServiceClient _serviceClient;
public MyService(IExternalServiceClient serviceClient)
{
_serviceClient = serviceClient;
}
然后将其用作:
var service = new MyService(new ExternalServiceClient ("WSHttpBinding_IMyService"));
在测试中
IExternalServiceClient mockObject = //construct mock with desired behabiour then pass to ctor
var service = new MyService(mockObject);
如果没有实现的接口和添加它的能力(因为它是外部的),那么您必须在虚拟性方面做一些技巧。谢谢Will-我正在考虑在项目中使用SimpleInjector for DI。然而,在MyService类中,什么是DI此ExternalServiceClient的最佳方式?构造函数注入是首选方式。请参阅我的编辑。使用DI工具,您甚至可以使用一些
条件绑定
将ExternalServiceClient
依赖注入字符串
基元类型。我不知道关于SimpleInjector
。真正的问题是没有任何对DI内核、容器或类中任何名称的引用。DI注入工具只能在您的合成根目录中引用,也就是说,您的程序入口点。构造函数注入对API用户来说更清晰,比基于注释的注入更容易记录,并且更易于测试。这两种类型的DI都比隐藏的依赖项好,但出于测试目的,我更喜欢在ctor.Will上显式地传递它们——在您的答案中我唯一看不到的是在哪里为ExternalServiceClientBtw指定(“WSHttpBinding_IMyService”),您的服务似乎做得不多。也许从ExternalServiceClient提取接口并直接作为依赖项传递更好;在安装WSHttp的地方-这将是每个方法的第一行,比如公共字符串GetName(坐标[]坐标)@KOL,这是个问题吗?是的,这是个问题-我在每个方法中都需要这个新的吗每次需要创建对象(MyService
)时都需要new
),或者重新创建上下文。依赖项注入工具(如Ninject、SimpleInjection、StructureMap等)用于避免每次都必须编写newthis(newthis)(newthat)(newhere(newthere()))
。