【问题标题】:Fake WCF-Service calls with FakeItEasy使用 FakeItEasy 伪造 WCF 服务调用
【发布时间】:2016-11-30 21:08:48
【问题描述】:

我想测试调用第三方 Web 服务的类。是否可以为此使用 FakeItEasy?

Wenn 我尝试从Reference.cs 伪造课程(自动生成),UnitTest 开始并且没有回来。

Reference.cs(自动生成)

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public partial class ws_AccessoryClient : System.ServiceModel.ClientBase<AccessoryService.ws_Accessory>,
        AccessoryService.ws_Accessory
    {
        public ws_AccessoryClient()
        {
        }

        public ws_AccessoryClient(string endpointConfigurationName) :
            base(endpointConfigurationName)
        {
        }

        public AccessoryService.ResponseMessageOf_ListOf_SomeMethodInfo SomeMethod(
            AccessoryService.RequestMessageOf_SomeMethod request)
        {
            return base.Channel.SomeMethod(request);
        }
    }

Test.cs

    [Test]
    public void DoBusinessLogicTryTest()
    {
        var accessoryProxy = A.Fake<ws_AccessoryClient>();
    }

【问题讨论】:

  • 一般来说,伪造类(而不是接口)可能很棘手,因为生成的伪造必须调用原始类的非虚拟行为。也许这就是这里发生的事情。考虑调试测试,然后“Break All”看看它卡在哪里。那么也许我们可以根据所学找到更好的答案。

标签: c# wcf fakeiteasy


【解决方案1】:

如前所述,您可能不想为单元测试做您打算做的事情,因为这会导致比可能使用模拟接口的单元测试所需的更多噪音。然而,它是一种有效的集成测试方法,这将允许您测试 WCF 接线是否按预期工作。如果您采用行为驱动的测试风格,并且希望尽可能少地进行模拟,它还允许您测试整个应用程序。

我自己使用这种方法来使用NSubstitute 旋转假端点,这在我的博客Hosting a Mock as a WCF service 中有介绍。您需要做的主要事情是启动ServiceHost,为其提供您要使用的端点地址,将上下文模式设置为单一并提供您要用作端点的模拟。

var serviceHost = new ServiceHost(mock, new[] { baseAddress });

serviceHost.Description.Behaviors
    .Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
serviceHost.Description.Behaviors
    .Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;

serviceHost.AddServiceEndpoint(typeof(TMock), new BasicHttpBinding(), endpointAddress);

我在测试中做的一件事是随机选择托管端点的端口,并在测试期间将地址注入我的应用程序。这样,您的测试将能够在其他机器上运行并构建服务器,而不会与正在使用的其他端口发生冲突。

查看您的示例后,您可能需要考虑使用 WCF ChannelFactory 来创建您的客户端,而不是使用具体的代理客户端类。 ChannelFactory 使用您提供的接口动态创建代理,并允许您使用服务接口将代理注入其依赖项。这将使单元测试更容易,并为您提供更解耦的设计。

【讨论】:

  • 完全同意重新渠道工厂而不是服务参考
【解决方案2】:

您不能(为什么要这样做?)。

如果您想验证您的被测类是否调用了服务,则将服务调用包装在一个类中,该类的唯一工作就是调用服务,并使用接口对其进行定义。

interface ICallTheService
{
    void CallTheService();
}

class ServiceCaller : ICallTheService
{
    void CallTheService()
    {
        // Call the service...
    }
}

然后你可以伪造这个类并验证你的测试类调用了 CallTheService 操作。

// fake the service caller and pass it into your service
var serviceCaller = A.Fake<ICallTheService>();

// Verify invocation
A.CallTo(() => serviceCaller.CallTheService()).MustHaveHappened();

我想在课堂上测试逻辑,取决于来自的响应 WCF-服务

这就是我认为您在关注点分离方面出错的地方。您的测试称为 DoBusinessLogicTryTest,但它依赖于 System.ServiceModel,这是一个基础架构问题。您的业​​务逻辑应该可以在没有这种依赖关系的情况下进行测试。如果您的被测类需要根据响应表现出不同的行为,您可以执行以下操作:

interface ICallTheService
{
    ServiceResponseModel CallTheService();
}

enum ServiceResponseModel
{
    Success,
    PartialSuccess,
    FailureCondition1,
    FailureCondition2,
    // etc...
}

然后您可以启动 ICallTheService 假以返回每个可能的响应并基于此测试您的类。

A.CallTo(() => serviceCaller.CallTheService()).Returns(ServiceResponseModel.Success);

例如,如果某些异常(在 WCF 中定义)被正确处理

这也与业务逻辑无关。异常的实际处理是 ICallTheService 实现的责任。事实上,我会为此介绍另一个类,它的工作是将 System.ServiceModel 中的various possible exceptions 转换为您的响应模型。例如

class WCFErrorResponseTranslator
{
    ServiceResponseModel TranslateWCFException (Exception ex)
    {
        if (ex.GetType() == typeOf(TimeoutException)) { return ServiceResponseModel.TimeOut; }
        /// etc
    }
}

然后可以单独测试此行为。

【讨论】:

  • 我想在课堂上测试逻辑,取决于来自 WCF-Service 的响应。例如,如果某些异常(在 WCF 中定义)被正确处理。端点(System.ServiceModel.Description.ServiceEndpoint)也在类中设置,应该在测试中伪造。谢谢。