工厂设计模式 - 不使用静态方法,因为单元测试是一个问题

我知道这个问题在stackoverflow中已经被问过好几次了,但不知何故,在找出解决方案时仍然遇到了一些麻烦。下面的例子我认为是一个很好的例子,有静态的方法

public class ConnectionFactory
{
     public static Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port)
    {
           //Some error checking
         switch(connectionType)
         {   
             case TCP:
                  return createTcpConnection(ipAddr, port);
             case UDP:
                  return createUdpConnection(ipAddr, port);
             case RTP:
                  return createRtpConnection(ipAddr, port);
             case SCTP:
                  return createRtpConnection(ipAddr, port);
             default:
                  break;
         }
    }

    // TcpConnection, RtpConnection, SctpConnection and UdpConnection implement interface Connection
    public Connection createTcpConnection()
    {
        Connection connection = new TcpConnection();
         .....
         .....
         return connection;
    }

    public Connection createUdpConnection()
    {
        Connection connection = new UdpConnection();
        .....
        .....
        return connection;
    }

    ....
    ....
}

假设我有一个像这样的通信服务

public class CommunicationService
{
    public void initConnectionPool(ConnectionType connectionType)
    {
        for(int i = 0; i < MAX_CONNECTIONS; i++)
             connectionList.add(ConnectionFactory.createConnection(connectionType, "domain.com", 40203));

        //Some more code here to do further processing
          ......
          ......
    }    

    //Some more methods
}

像这样,不同的通信服务可以创建和维护多种类型的连接。

我想测试initConnectionPool方法,在单元测试环境中,套接字创建肯定会失败。

我可以将ConnectionFactory更改为一个具体的类并模拟它。但是,这种情况不是使用静态方法创建类的好情况吗?我不是在ConnectionFactory中维护任何状态。那么,如果使用静态方法可能合适,可能会导致测试问题,我们什么时候使用静态方法呢?还是在这里使用静态方法不合适?

编辑:我使用的解决方案

public class CommunicationService
{
    public void initConnectionPool(ConnectionType connectionType)
    {
        for(int i = 0; i < MAX_CONNECTIONS; i++)
             connectionList.add(connectToHost(connectionType));

        //Some more code here to do further processing
          ......
          ......
    }    

    public Connection connectToHost(ConnectionType connectionType)
    {
        ConnectionFactory.createConnection(connectionType, "domain.com", 40203)
    }
    //Some more methods
}

在测试中,覆盖 connectToHost 并返回一个模拟。


答案 1

如果你使用像JMockIt这样的库,那么你就可以模拟出静态方法进行单元测试。


答案 2

我认为你应该阅读这篇文章:静态方法对可测试性来说是死的(谷歌测试博客)。

尽管您的类不维护任何状态信息,但我建议您创建具体的类并如下所示:ConnectionFactory

public class ConnectionFactory
{
    public Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port)
    {
         //Some error checking
         switch(connectionType)
         {   
             case TCP:
                  return createTcpConnection(ipAddr, port);
             case UDP:
                  return createUdpConnection(ipAddr, port);
             case RTP:
                  return createRtpConnection(ipAddr, port);
             case SCTP:
                  return createRtpConnection(ipAddr, port);
             default:
                  break;
         }
    }

    // TcpConnection, RtpConnection, SctpConnection and UdpConnection implement interface Connection
    public Connection createTcpConnection()
    {
        Connection connection = new TcpConnection();
        ...
        return connection;
    }

    public Connection createUdpConnection()
    {
        Connection connection = new UdpConnection();
        ...
        return connection;
    }
    ...    
}

public class CommunicationService
{
    private ConnectionFactory connectionFactory;

    public CommunicationService()
    {
        this(new ConnectionFactory());
    }

    public CommunicationService(ConnectionFactory factory)
    {
        connectionFactory = factory;
    }

    public void initConnectionPool(ConnectionType connectionType)
    {
        for(int i = 0; i < MAX_CONNECTIONS; i++)
             connectionList.add(connectionFactory.createConnection(connectionType, "domain.com", 40203));
        ...
    }    
    ...
}

代码的其余部分根本不会更改,但出于测试目的,您将能够创建TestConnectionFactory类:

public class TestConnectionFactory : ConnectionFactory
{
    public override Connection createTcpConnection()
    {
        ...
        return testTcpConnection;
    }

    public override Connection createUdpConnection()
    {
        ...
        return testUdpConnection;
    }
}

并用它来测试,如下所示:CommunicationService

CommunicationService service = new CommunicationService(new TestConnectionFactory());
// Tests
...

推荐