C# 如何模拟VisualStudio自动生成的SOAP客户端?
我有一个自动生成的SOAP客户端。它是由visual studio向导添加连接的服务->Microsoft WCF Web服务参考提供程序生成的。我想模拟那个客户端,所以当调用一个方法时,预定义的结果将以SOAP响应的格式返回。不幸的是,我无法让它工作-我的结果要么为null,而不是在.Returns中定义的,要么我得到一个异常C# 如何模拟VisualStudio自动生成的SOAP客户端?,c#,unit-testing,.net-core,moq,soap-client,C#,Unit Testing,.net Core,Moq,Soap Client,我有一个自动生成的SOAP客户端。它是由visual studio向导添加连接的服务->Microsoft WCF Web服务参考提供程序生成的。我想模拟那个客户端,所以当调用一个方法时,预定义的结果将以SOAP响应的格式返回。不幸的是,我无法让它工作-我的结果要么为null,而不是在.Returns中定义的,要么我得到一个异常 我试图在我的设计中应用干净的架构。因此,这个SOAP客户机位于我的基础结构层,我在那里为它创建了一个存储库。此存储库创建DTO,以便可以将它们分派到我的持久性中。存储库
我试图在我的设计中应用干净的架构。因此,这个SOAP客户机位于我的基础结构层,我在那里为它创建了一个存储库。此存储库创建DTO,以便可以将它们分派到我的持久性中。存储库通过依赖项注入接收SOAP客户端。我还希望对存储库进行测试,以验证DTO生成是否正确。因此,我想模拟这个SOAP服务,以便将其提供给存储库并测试返回的DTO 自动生成的界面:
public interface ApplicationSoap
{
[System.ServiceModel.OperationContractAttribute(Action = "http://Application/GetAppVersion", ReplyAction = "*")]
Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(ExtApp.GetAppVersionRequest request);
}
和自动生成的客户端类:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1-preview-30514-0828")]
public partial class ApplicationSoapClient : System.ServiceModel.ClientBase<ExtApp.ApplicationSoap>, ExtApp.ApplicationSoap
{
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
System.Threading.Tasks.Task<ExtApp.GetAppVersionResponse> ExtApp.ApplicationSoap.GetAppVersionAsync(ExtApp.GetAppVersionRequest request)
{
return base.Channel.GetAppVersionAsync(request);
}
public System.Threading.Tasks.Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(int appVer)
{
ExtApp.GetAppVersionRequest inValue = new ExtApp.GetAppVersionRequest();
inValue.Body = new ExtApp.GetAppVersionRequestBody();
inValue.Body.appVer = appVer;
return ((ExtApp.ApplicationSoap)(this)).GetAppVersionAsync(inValue);
}
}
我想模拟ApplicationSoapClient和方法GetApplicationVersionAsync。
在不同的尝试之后,我在我的测试课上得出以下结论:
private ExtApp.AppVersion[] _response =
new ExtApp.AppVersion[]
{
new ExtApp.AppVersion
{
VersionNumber = 1,
StartDate = DateTime.Parse("2010-01-01"),
EndDate = DateTime.Parse("2015-12-31")
},
};
private Mock<ExtApp.ApplicationSoap> _client = new Mock<ExtApp.ApplicationSoap>();
public TestClass()
{
var body = new ExtApp.GetAppVersionRequestBody(It.IsAny<int>());
var request = new ExtApp.GetAppVersionRequestRequest(body);
_client
.Setup(s => s.GetAppVersionAsync(request))
.Returns(
Task.FromResult(
new ExtApp.GetAppVersionResponse(
new ExtApp.GetAppVersionResponseBody(_response)))
);
}
[Fact]
public void TestAppVersionDownload()
{
var request = new ExtApp.GetAppVersionRequest(
new ExtApp.GetAppVersionRequestBody(1));
var result = _clientSoap.Object.GetAppVersionAsync(request)
.Result; //returns null instead of defined in Returns section
Assert.True(result.Body.GetAppVersionResult.Length == 2);
}
它运行,但调用的结果为null。我希望返回包含数组的非null Body属性的对象。我正在寻找如何使这件事起作用的建议。您的SOAP客户端合同是:
public interface ApplicationSoap
{
[System.ServiceModel.OperationContractAttribute(Action = "http://Application/GetAppVersion", ReplyAction = "*")]
Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(ExtApp.GetAppVersionRequest request);
}
您可以在存储库中将其用作依赖项,如下所示:
public class Repository
{
private readonly IApplicationSoap _client;
public Repository(IApplicationSoap client) { _client = client; }
public async Task<AppVersion> GetAppVersionAsync(int version)
{
var request = new GetAppVersionRequest(new GetAppVersionRequestBody(version));
var response = await _client.GetAppVersionAsync(request);
return new AppVersion
{
Version = response.Body.Version,
StartDate = response.Body.StartDate,
EndDate = response.Body.EndDate
};
}
}
在这种情况下,您可能需要测试将输入转换为请求的代码以及将响应转换为DTO的代码。这是唯一属于您的代码,而不是由工具生成的代码。为此,您需要在存储库测试中模拟SOAP客户机契约,并让它返回您想要的响应:
[Fact]
public async Task GetAppVersionAsync()
{
// arrange
var client = new Mock<IApplicationSoap>(); // mock the interface, not the class!
var result = new AppVersion
{
Version = 1,
StartDate = DateTime.Parse("2010-01-01"),
EndDate = DateTime.Parse("2015-12-31")
};
client.Setup(x => x.GetAppVersionAsync(It.IsAny<GetAppVersionRequest>))
.Returns(Task.FromResult(new GetAppVersionResponse(new GetAppVersionResponseBody(result))));
var repository = new Repository(soapApp);
// act
var dto = await repository.GetAppVersionAsync(1);
// assert (verify the DTO state)
Assert.Equal(1, dto.VersionNumber);
Assert.Equal(new DateTime(2010, 1, 1), dto.StartDate);
Assert.Equal(new DateTime(2015, 12, 31), dto.EndDate);
}
然而。。。您可以这样做并不意味着您应该这样做。您的SOAP客户端合同是:
public interface ApplicationSoap
{
[System.ServiceModel.OperationContractAttribute(Action = "http://Application/GetAppVersion", ReplyAction = "*")]
Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(ExtApp.GetAppVersionRequest request);
}
您可以在存储库中将其用作依赖项,如下所示:
public class Repository
{
private readonly IApplicationSoap _client;
public Repository(IApplicationSoap client) { _client = client; }
public async Task<AppVersion> GetAppVersionAsync(int version)
{
var request = new GetAppVersionRequest(new GetAppVersionRequestBody(version));
var response = await _client.GetAppVersionAsync(request);
return new AppVersion
{
Version = response.Body.Version,
StartDate = response.Body.StartDate,
EndDate = response.Body.EndDate
};
}
}
在这种情况下,您可能需要测试将输入转换为请求的代码以及将响应转换为DTO的代码。这是唯一属于您的代码,而不是由工具生成的代码。为此,您需要在存储库测试中模拟SOAP客户机契约,并让它返回您想要的响应:
[Fact]
public async Task GetAppVersionAsync()
{
// arrange
var client = new Mock<IApplicationSoap>(); // mock the interface, not the class!
var result = new AppVersion
{
Version = 1,
StartDate = DateTime.Parse("2010-01-01"),
EndDate = DateTime.Parse("2015-12-31")
};
client.Setup(x => x.GetAppVersionAsync(It.IsAny<GetAppVersionRequest>))
.Returns(Task.FromResult(new GetAppVersionResponse(new GetAppVersionResponseBody(result))));
var repository = new Repository(soapApp);
// act
var dto = await repository.GetAppVersionAsync(1);
// assert (verify the DTO state)
Assert.Equal(1, dto.VersionNumber);
Assert.Equal(new DateTime(2010, 1, 1), dto.StartDate);
Assert.Equal(new DateTime(2015, 12, 31), dto.EndDate);
}
然而。。。仅仅因为你能做到这一点并不意味着你应该这样做。我的第一个问题是为什么?确实如此。此测试不能证明您需要模拟SOAP客户端。如果测试通过,它只能证明Moq按预期工作,我们都知道这一点,并且您知道如何使用它。或者,如果您模拟服务器,您的测试将证明WCF是有效的。对此也毫无疑问。你想证明什么是有效的?这可能是一个错误。我试图在我的设计中应用干净的体系结构。因此,这个SOAP客户机位于我的基础结构层,我在那里为它创建了一个存储库。此存储库创建DTO,以便可以将它们分派到我的持久性中。存储库通过依赖项注入接收SOAP客户端。我还希望对存储库进行测试,以验证DTO生成是否正确。因此,我想模拟这个SOAP服务,以便将其提供给存储库并测试返回的DTO。我编辑了这个问题并添加了这个解释。很明显,我对这一观点持批评态度。我的第一个问题是为什么?确实如此。此测试不能证明您需要模拟SOAP客户端。如果测试通过,它只能证明Moq按预期工作,我们都知道这一点,并且您知道如何使用它。或者,如果您模拟服务器,您的测试将证明WCF是有效的。对此也毫无疑问。你想证明什么是有效的?这可能是一个错误。我试图在我的设计中应用干净的体系结构。因此,这个SOAP客户机位于我的基础结构层,我在那里为它创建了一个存储库。此存储库创建DTO,以便可以将它们分派到我的持久性中。存储库通过依赖项注入接收SOAP客户端。我还希望对存储库进行测试,以验证DTO生成是否正确。因此,我想模拟这个SOAP服务,以便将其提供给存储库并测试返回的DTO。我编辑了这个问题并添加了这个解释。显然,我对这个想法持开放态度。一般来说,它与我编写的代码几乎相同,只是您使用的是一个自动生成接口中不存在的带有签名的方法:GetAppVersionAsyncint appVersion此方法存在于自动生成的类中。如果我手动将其添加到接口中,那么实际上,这是可行的。但这是次优的,因为客户端的重新生成将删除添加的方法,任何进行重新生成的开发人员都必须记住手动添加它。如何使用现有的Get实现这一点
AppVersionAsyncExtApp.GetAppVersionRequest请求?另外,既然您说过这可能不应该做-您建议我如何解决我的存储库测试问题?或者我应该遵循什么样的好标准。我当然愿意听取建议。谢谢你的意见。我对使用这个不存在的方法感到不好。我编辑了答案来解决这个问题。我认为编写这样的单元测试只测试转换太费劲了。但这是一个不太喜欢单元测试的人的个人观点。我会测试比这更复杂的逻辑。因为我在这个项目上与其他开发人员合作,而这是一个全新的项目,很多事情都会改变。由于我也在使用automapper,我希望有一种自动检查一致性的方法。这就是选择。另外,我期待着服务很快会改变,所以这里会有更多的发展。还有你的回答——这对我来说是个难题,当谈到最低起订量时。谢谢最后还获得了对所有地方进行注释的分数。通常,它与我编写的代码几乎相同,只是您使用的方法具有自动生成的接口中不存在的签名:GetAppVersionAsyncint appVersion此方法存在于自动生成的类中。如果我手动将其添加到接口中,那么实际上,这是可行的。但这是次优的,因为客户端的重新生成将删除添加的方法,任何进行重新生成的开发人员都必须记住手动添加它。我如何使用现有的GetAppVersionAsyncExtApp.GetAppVersionRequest请求实现这一点?另外,既然您说过这可能不应该实现,那么您建议我如何解决我的存储库测试问题?或者我应该遵循什么样的好标准。我当然愿意听取建议。谢谢你的意见。我对使用这个不存在的方法感到不好。我编辑了答案来解决这个问题。我认为编写这样的单元测试只测试转换太费劲了。但这是一个不太喜欢单元测试的人的个人观点。我会测试比这更复杂的逻辑。因为我在这个项目上与其他开发人员合作,而这是一个全新的项目,很多事情都会改变。由于我也在使用automapper,我希望有一种自动检查一致性的方法。这就是选择。另外,我期待着服务很快会改变,所以这里会有更多的发展。还有你的回答——这对我来说是个难题,当谈到最低起订量时。谢谢也终于赢得了各处评论的分数