2011-07-07 8 views
14

मैं जानता हूँ कि इस सवाल 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 
} 

इस तरह की तरह एक CommunicationService है, विभिन्न संचार सेवाओं बना सकते हैं और एक से अधिक प्रकार बनाए रख सकते हैं कनेक्शन।

मैं initConnectionPool विधि का परीक्षण करना चाहते हैं और एक इकाई परीक्षण वातावरण में, सॉकेट निर्माण निश्चित रूप से असफल हो जायेगी।

मैं एक ठोस वर्ग के लिए 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 overrode और एक नकली लौट आए।

+0

आपका कनेक्शन फैक्ट्री पहले से ही एक ठोस (गैर-सार) वर्ग है। स्थैतिक के विपरीत 'गतिशील' होगा, हालांकि मुझे अक्सर इस संदर्भ में उपयोग नहीं किया जाता है। केवल स्थिर विधियों वाला एक वर्ग आमतौर पर 'उपयोगिता वर्ग' के रूप में संदर्भित होता है। –

उत्तर

1

मैं अपने बेहतर यहाँ पर स्थिर विधियों का उपयोग करने लगता है।

परीक्षण वातावरण या किसी अन्य वातावरण में - ConnectionFactory गुण के विभिन्न सेट का उपयोग प्रारंभ किया जाना चाहिए।

तो प्रभाव में आप गुण अलग वातावरण के लिए (connectiontype, बंदरगाह आदि से युक्त) के विभिन्न सेट होना चाहिए और आप उचित एक का उपयोग कर init कर सकते हैं।

3

आप JMockIt की तरह एक पुस्तकालय का उपयोग करते हैं तो आप इकाई परीक्षण के लिए स्थिर तरीकों बाहर नकली कर सकते हैं।

0

अपरिवर्तनीय ConnectionFactory वर्ग एक ConnectionType उदाहरण, आईपी पते और पोर्ट के साथ initialised करें:

public class ConnectionFactory { 
    private final ConnectionType connectionType; 
    private final String ipAddr; 
    private final Integer port; 

    public ConnectionFactory(final ConnectionType connectionType) { 
      this.connectionType = connectionType; 
      this.ipAddr = ipAddr; 
      this.port = port; 
    } 

    public Connection createConnection() { 
     // TODO your code here 
     ... 
    } 
    .... 
} 

इस तरह विधि initConnectionPool एक तर्क के रूप ConnectionFactory का एक उदाहरण ले जाएगा और आप कोई समस्या नहीं गुजर होगा जो कुछ भी नकली/स्टब/कंक्रीट कार्यान्वयन आपको परीक्षण और अन्य परिस्थितियों के दौरान आवश्यक है।

1

I हमेशा मेरे सिंगलेट्स facades बनाते हैं।

public interface IConnectionFactory //i know that it's not a correct java naming 
{ 
    Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port); 
} 

public class ConnectionFactory 
{ 
    private IConnectionFactory _implementation; 

    public static Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port) 
    { 
     return _implementation.createConnection(connectionType, ipAdd, port); 
    } 

    //DO assign a factory before doing anything else. 
    public static void AssignFactory(IConnectionFactory implementation) 
    { 
     _implementation = implementation; 
    } 
} 

इस तरह कर रहा एकमात्र लचीला बनाता है और आप आसानी से कार्यान्वयन बदल सकते हैं।

+0

क्यों गुइस की तरह आईओसी ढांचे की उम्र में एक सिंगलटन मुखौटा एक अच्छा विकल्प होगा? – 01es

+1

ताकि आपके पास अपने कोड में सुई अंक न हों। – irreputable

+0

क्यों? चूंकि सभी एप्लिकेशन आईओसी का उपयोग नहीं करते हैं। विशेष रूप से विरासत अनुप्रयोगों। Facades सिंगलेट्स अधिक स्टेनलेस बनाता है। – jgauffin

0

जहां तक ​​परीक्षण की बात है, इसे हल करने

public class ConnectionFactory 

    static boolean test = false; 

    public static Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port) 

     if(test) ... 
     else ... 


public class ConnectionFactoryTest // in the same package 
    public static void enableTest(){ ConnectionFactory.test=true; } 

// in some other test classes 
    ConnectionFactoryTest.enableTest(); 
    ConnectionFactory.createConnection(...); 

test झंडा नहीं है volatile आसान है। यह उत्पादन कोड में सुरक्षित धागा है। सबसे अधिक संभावना if(test) JVM द्वारा बंद अनुकूलित कर दिया जाएगा।

3

मुझे लगता है कि आपको यह लेख पढ़ना चाहिए: Static Methods are Death to Testability (Google परीक्षण ब्लॉग)।

के बावजूद की अपनी 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)); 
     ... 
    }  
    ... 
} 

अपने कोड के बाकी सब पर नहीं बदलेगा, लेकिन के लिए

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

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

और CommunicationService इस तरह के परीक्षण के लिए इसका इस्तेमाल करते हैं:

परीक्षण प्रयोजनों आप TestConnectionFactory वर्ग बनाने के लिए सक्षम हो जाएगा
+2

ब्लॉगर गलत है। एक स्थिर विधि का मज़ाक उड़ाते हुए पूरे कॉल ग्राफ़ को मॉक करने की आवश्यकता नहीं होती है। मॉकिंग कनेक्शन फ़ैक्टरी को मॉकिंग सॉक एपीआई की आवश्यकता नहीं है। स्थिर या नहीं, जटिलता वही है। – irreputable

+1

हां स्थिर परीक्षण वास्तव में एक दर्द है जब परीक्षण की बात आती है। मैं कनेक्शन इंटरफ़ेस को एक इंटरफ़ेस को कार्यान्वित करके एक ठोस वर्ग बनाकर अपने समाधान की लाइनों में सोच रहा था और उसी इंटरफ़ेस को कार्यान्वित करने और सेवाओं में सिंगलटन के रूप में इंजेक्शन देने वाले यूनिट परीक्षणों के लिए टेस्टकोनक्शन फ़ैक्टरी है। – Prasanna

+0

@ प्रसाद, इंटरफ़ेस भी बेहतर है। वैसे भी मैं एक ठोस वर्ग का उपयोग करेंगे। तय करें कि आपके मामले में सबसे अच्छा क्या होगा। – bniwredyc

0

एक पैटर्न जो आपको अनुकूल हो सकता है singleton pattern है, जो आपको दोनों दुनिया के सर्वश्रेष्ठ प्रदान करता है: उदाहरण विधियों के साथ स्थिर-जैसे व्यवहार। इसके सरलतम रूप में, यह इस तरह दिखता है:

public class ConnectionFactory { 

    private static ConnectionFactory INSTANCE = new ConnectionFactory(); 

    private ConnectionFactory() {} // private constructor 

    public static ConnectionFactory getInstance() { 
     return INSTANCE; 
    } 

    public void someMethod() { 
     ... 
    } 
} 

कॉलर्स इस तरह इसका इस्तेमाल:

ConnectionFactory.getInstance().someMethod(); 

java.util.Calendar एक JDK वर्ग इस पद्धति का उपयोग करता है का एक उदाहरण है

0

सबसे पहले: मुझे आपके कनेक्शन फ़ैक्टरी के साथ कोई समस्या नहीं दिखाई दे रही है जब तक कि इसमें कोई भी राज्य न हो (जो ऐसा लगता है)। परीक्षण के साथ आपके पास समस्याएं इस मामले में स्थैतिक फैक्ट्री विधि से उत्पन्न नहीं होती हैं।

यूनिट परीक्षण कोड जो फ़ाइल सिस्टम, डेटाबेस या नेटवर्क जैसे पर्यावरण तत्वों के साथ सीधे इंटरैक्ट करता है, हमेशा कठिन होता है। इस मामले में आपको ऐसा करने के लिए सॉकेट निर्माण की नकल करने की आवश्यकता होगी और शायद यह प्रयास के लायक नहीं है। यहां तक ​​कि यदि आपने ऐसा किया है, तो परीक्षण कोड बड़ा, बनाए रखने और भंगुर करने के लिए कठिन होगा।

पर पुनर्विचार करें तुम क्यों इस वर्ग के लिए एक इकाई परीक्षण लिखने के लिए चाहते हैं ...

इस संबंध कारखाने और अधिक आसानी से एक एकीकरण परीक्षण द्वारा कवर किया जा सकता है। आप एक स्थानीय सॉकेट सर्वर स्थापित कर सकते हैं, प्राप्त इनपुट रिकॉर्ड कर सकते हैं और आउटपुट भेजा जा सकता है। नीचे की तरह कुछ:

public class TestServer { 

    private int port; 
    private String[] responses; 
    private List<String> inputLines = new ArrayList<String>(); 

    public TestServer(int port, String ... responses) { 
     this.port = port; 
     this.responses = responses; 
    } 

    public void run() throws IOException { 

     ServerSocket serverSocket = new ServerSocket(port); 
     Socket clientSocket = serverSocket.accept(); 
     PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); 
     BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
     String inputLine; 
     int i = 0; 
     while ((inputLine = in.readLine()) != null) { 
      inputLines.add(inputLine); 
      out.println(responses[i++]); 
     } 
     out.close(); 
     in.close(); 
     clientSocket.close(); 
     serverSocket.close(); 
    } 

    public List<String> getInputLines() { 
     return inputLines; 
    } 
} 

आपका परीक्षण कोड के नीचे की तरह कुछ दिख सकता है:

// setup 
String sentInput = "hello"; 
String sentOutput = "hi there!"; 
int port = 4444; 
TestServer server = new TestServer(port, sentOutput); 
server.run(); 

// test 
Connection connection = ConnectionFactory.createConnection(ConnectionType.TCP, "localhost", port); 
// not sure of your Connection's API 
connection.open(); 
String receivedOutput = connection.send(sentInput); 
connection.close(); 

// verify 
List<String> inputLines = server.getInputLines(); 
assertEquals(sentInput , inputLines.get(0)); 
assertEquals(sentOutput, receivedOutput); 

आशा इस मदद करता है।

संपादित करें: ठीक है, इसलिए मैंने आपके प्रश्न को थोड़ा सा गलत समझा, क्षमा करें। ऊपर दृष्टिकोण अभी भी एक वैध समाधान है जो मुझे लगता है। यह आपको अपनी स्थैतिक फैक्ट्री विधियों को रखने और एकीकरण परीक्षण चलाने की अनुमति देगा। हालांकि, आपकी इच्छा unittest आपकी सेवा विधि पूरी तरह से समझ में आता है। आपके द्वारा चुने गए समाधान (आपकी सेवा में कनेक्ट टॉहोस्ट ओवरराइड करना) मेरे लिए बहुत अच्छा लगता है।एक अन्य तरीका यह करने के लिए इस तरह है:

एक अंतरफलक बनाएं निर्भरता रैप करने के लिए:

public interface ConnectionProvider { 
    Connection provideConnection(ConnectionType connectionType); 
} 

लपेटें अपने कारखाने के लिए कॉल और एक सेटर जोड़ें: बजाय अपने connectionProvider कॉल

private ConnectionProvider connectionProvider = new ConnectionProvider() { 
    public Connection provideConnection(ConnectionType connectionType) { 
     return ConnectionFactory.createConnection(connectionType, "domain.com", 40203); 
    } 
}; 

public void setConnectionProvider(ConnectionProvider connectionProvider) { 
    this.connectionProvider = connectionProvider; 
} 

कारखाने को सीधे कॉल करने के लिए:

public void initConnectionPool(ConnectionType connectionType) { 
    for (int i = 0; i < MAX_CONNECTIONS; i++) { 
     connectionList.add(connectionProvider.provideConnection(connectionType)); 
    } 
    // Some more code here to do further processing 
    // ...... 
    // ...... 
} 

अपने unittest में, प्रदाता को प्रदाता का उपयोग करें ई एक नकली

+0

मैं कनेक्शन फैक्टरी का परीक्षण नहीं करना चाहता था लेकिन संचार सेवा में विधि जो कन्फेक्शन फैक्ट्री में स्थिर विधि कॉल के साथ कसकर मिलती है – Prasanna

0

एक और आम अभ्यास अपने स्वयं के सहायक वर्ग को परिभाषित करना है जो स्थिर विधियों और रचनाकारों को कॉल कर सकता है। यदि आपको कुछ तृतीय-पक्ष पुस्तकालयों का नकल करने की आवश्यकता है, तो यह काफी उपयोगी है, जिसे आप फिर से लिख नहीं सकते हैं।

public class CommunicationService { 
    private final CommunicationServiceHelper helper; 
    public CommunicationService() { 
     this(new CommunicationServiceHelper()); 
    } 

    @VisibleForTesting 
    CommunicationService(CommunicationServiceHelper helper) { 
     this.helper = helper; 
    } 

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

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

    //Some more methods 

    @VisibleForTesting 
    static class CommunicationServiceHelper { 
     public Connection createConnection(ConnectionType connectionType, String ipAddr, Integer port) { 
      return ConnectionFactory.createConnection(connectionType, ipAddr, port); 
     } 
    } 
} 
संबंधित मुद्दे