【问题标题】:How to mock SOAP client that was auto-generated by Visual Studio?如何模拟由 Visual Studio 自动生成的 SOAP 客户端?
【发布时间】:2019-07-29 19:24:35
【问题描述】:

我有一个自动生成的 SOAP 客户端。它是由 Visual Studio 向导生成的(添加连接的服务 -> Microsoft WCF Web 服务参考提供程序)。我想模拟那个客户端,所以当一个方法被调用时,一个预定义的结果将以 SOAP 响应的格式返回。不幸的是,我无法让它工作——我的结果要么为空(而不是在 .Returns 中定义),要么出现异常。

我正在尝试在我的设计中应用干净的架构。所以这个 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。我期望取回具有非空 Body 属性的对象,其中包含数组。我正在寻找如何使这件事发挥作用的建议。

【问题讨论】:

  • 我的第一个问题是为什么?
  • 确实如此。该测试不能证明您需要模拟 SOAP 客户端。如果测试通过,它只会证明 Moq 可以按预期工作(我们都知道这一点)并且您知道如何使用它。或者,如果您改为模拟服务器,您的测试将证明 WCF 有效。这一点也毋庸置疑。你想证明什么是有效的?
  • 这可能是XY problem
  • 我正在尝试在我的设计中应用干净的架构。所以这个 SOAP 客户端登陆了我的基础设施层,在那里我为它创建了一个存储库。此存储库创建 DTO,因此可以将它们分派给我的持久性。存储库通过依赖注入接收 SOAP 客户端。我还想对存储库进行测试,只是为了验证 DTO 生成是否正确。所以,我想模拟这个 SOAP 服务,所以我可以将它提供给存储库并测试返回的 DTO。我编辑了问题并添加了这个解释。我显然愿意批评这个想法。

标签: c# unit-testing .net-core moq soap-client


【解决方案1】:

您的 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 的代码。这是你的唯一的代码(而不是由工具生成的)。为此,您需要在您的存储库测试中模拟(实际上是 stub)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);
}

但是...仅仅因为您可以这样做并不意味着您应该这样做。

【讨论】:

  • 一般来说它和我做的代码几乎一样,除了你使用的签名方法在自动生成的接口中不存在:GetAppVersionAsync(int appVersion)这个方法存在于自动生成的类中。如果我手动将其添加到界面中,那么确实可以。但这是次优的,因为客户端的重新生成将删除添加的方法,并且任何进行重新生成的开发人员都必须记住手动添加它。如何使用现有的 GetAppVersionAsync(ExtApp.GetAppVersionRequest request) 实现这一目标?
  • 另外,既然你说这可能不应该这样做 - 你建议我应该如何解决我的存储库测试问题?或者我应该遵循什么好的标准。我绝对愿意接受建议。感谢您的意见。
  • 我不喜欢使用不存在的方法。我编辑了答案来解决这个问题。
  • 我认为编写这种只测试转换的单元测试太费力了。但这是不喜欢单元测试的人的个人意见。我会测试比这更复杂的逻辑。
  • 因为我在这个项目上与其他开发人员合作,而且这是一个新建项目,很多事情都必然会改变。而且由于我也在使用 automapper,我希望有一种自动检查一致性的方法。从而做出选择。另外,我期待服务很快就会改变,所以这里会有更多的发展。而你的回答——当谈到起订量时,这对我来说是个问题。谢谢!!! (也终于赚到了到处评论的积分)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 1970-01-01
相关资源
最近更新 更多