单元测试WCF客户端

Eri*_*c B 6 c# wcf unit-testing

我正在使用当前不使用任何依赖注入的代码,并通过WCF客户端进行多个服务调用.

public class MyClass
{
    public void Method()
    {
        try
        {
            ServiceClient client = new ServiceClient();
            client.Operation1();
        }
        catch(Exception ex)
        {
            // Handle Exception
        }
        finally
        {
            client = null;
        }

        try
        {
            ServiceClient client = new ServiceClient();
            client.Operation2();
        }
        catch(Exception ex)
        {
            // Handle Exception
        }
        finally
        {
            client = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的目标是通过使用依赖注入使这个代码单元可测试.我的第一个想法是简单地将服务客户端的实例传递给类构造函数.然后在我的单元测试中,我可以创建一个模拟客户端用于测试目的,但不会向Web服务发出实际请求.

public class MyClass
{
    IServiceClient client;

    public MyClass(IServiceClient client)
    {
        this.client = client;
    }

    public void Method()
    {
        try
        {
            client.Operation1();
        }
        catch(Exception ex)
        {
            // Handle Exception
        } 

        try
        {
            client.Operation2();
        }

        catch(Exception ex)
        {
            // Handle Exception
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我意识到这会以一种影响其原始行为的方式更改代码,基于此问题的信息:在WCF出现故障后重新使用WCF中的客户端类

在原始代码中,如果对Operation1的调用失败并且客户端处于故障状态,则会创建一个新的ServiceClient实例,并且仍将调用Operation2.在更新的代码中,如果对Operation1的调用失败,则重用相同的客户端来调用Operation2,但如果客户端处于故障状态,则此调用将失败.

是否可以在保持依赖注入模式的同时创建客户端的新实例?我意识到反射可以用来从字符串中实例化一个类,但我觉得反射不是解决这个问题的正确方法.

k.m*_*k.m 7

您需要注入工厂而不是实例本身:

public class ServiceClientFactory : IServiceClientFactory
{
    public IServiceClient CreateInstance()
    {
        return new ServiceClient();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在MyClass每次需要时只需使用factory来获取实例:

// Injection
public MyClass(IServiceClientFactory serviceClientFactory)
{
    this.serviceClientFactory = serviceClientFactory;
}

// Usage
try
{
    var client = serviceClientFactory.CreateInstance();
    client.Operation1();
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用Func<IServiceClient>委托注入函数返回此类客户端,以便您可以避免创建额外的类和接口:

// Injection
public MyClass(Func<IServiceClient> createServiceClient)
{
    this.createServiceClient = createServiceClient;
}

// Usage
try
{
    var client = createServiceClient();
    client.Operation1();
}

// Instance creation
var myClass = new MyClass(() => new ServiceClient());
Run Code Online (Sandbox Code Playgroud)

在你的情况下Func<IServiceClient>应该足够了.一旦实例创建逻辑变得更复杂,那么将是重新考虑显式实现工厂的时间.