[英]Unit Testing Service Layer in .NET
我正在开发一个新的 MVC 5 应用程序。 我的项目中有一个集成层,其中有一个对外部 Web 服务的服务引用。
然后我创建了一个具有一些方法的接口 - 然后我有一个服务类,如下所示:
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
类中对其进行了初始化,这不允许您轻松切换依赖项,导致您在测试它时头疼。
真正的问题应该是:
我该如何设计我的服务以使其易于测试?
答案是Dependency Injection
。
因为您将能够模拟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;
}
这样,您就可以随意替换您的依赖项,因此可以使用模拟。
使用NUnit
和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;
}
通常,使用参数化构造函数来注入对对象构造的依赖是一种很好的做法(因为您对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);
如果没有实现的接口和添加它的能力(因为它是外部的),你必须用虚拟性做一些技巧。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.