2015-03-02 9 views
9

मैं इसजावा में, मैं ServiceLoader का उपयोग करके लोड की गई सेवा का नकल कैसे कर सकता हूं?

ServiceLoader.load(SomeInterface.class) 

की तरह कोड है कि कुछ एक विरासत जावा अनुप्रयोग है और मैं इस कोड का उपयोग करने के लिए के लिए SomeInterface की एक नकली कार्यान्वयन प्रदान करना चाहते हैं। मैं मॉकिटो मॉकिंग फ्रेमवर्क का उपयोग करता हूं।

दुर्भाग्यवश मैं विरासत कोड को बदलने में असमर्थ हूं, और मैं कुछ भी स्थिर रूप से जोड़ना नहीं चाहता (उदाहरण के लिए। मेटा-आईएनएफ में चीजें जोड़ना)।

क्या परीक्षण के भीतर से ऐसा करने का कोई आसान तरीका है, यानी। परीक्षण के रनटाइम पर?

+0

तुम सिर्फ आवेदन कोड बदलने में सक्षम हैं परीक्षण के उद्देश्य के लिए? – SpaceTrucker

उत्तर

6

आप स्थिर तरीकों उपहास करने के लिए Mockito के साथ PowerMockito उपयोग कर सकते हैं:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(ServiceLoader.class) 
public class PowerMockingStaticTest 
{ 
    @Mock 
    private ServiceLoader mockServiceLoader; 

    @Before 
    public void setUp() 
    { 
     PowerMockito.mockStatic(ServiceLoader.class); 
     Mockito.when(ServiceLoader.load(Mockito.any(Class.class))).thenReturn(mockServiceLoader); 
    } 

    @Test 
    public void test() 
    { 
     Assert.assertEquals(mockServiceLoader, ServiceLoader.load(Object.class)); 
    } 
} 
+0

यह शायद सबसे अच्छा जवाब है, हालांकि मुझे अतीत में कोड को छेड़छाड़ करने वाले ढांचे के साथ बुरे अनुभव हुए हैं (उदाहरण के लिए जेमॉकिट बहुत अजीब व्यवहार का कारण बनता है)। मैं शायद कई दर्जन स्थानों को लपेटूंगा, इस विरासत कोड को एक संकेत के साथ बुलाया जाता है जो मुझे परीक्षण से विरासत कोड का नकली संस्करण डालने की अनुमति देगा। –

+1

@GraemeMoss: बस एक और विचार, यदि सेवा लोडर-विधि को निर्माण समय पर बुलाया जाता है, और आप युक्त वर्ग के निर्माण और लोड सेवा सेवा के वास्तविक उपयोग के बीच "बीच में" प्राप्त कर सकते हैं, तो आप शायद प्रतिस्थापित करने के लिए प्रतिबिंब का उपयोग कर सकते हैं वास्तव में उपयोग किए जाने से पहले, आपके मैक्स के संदर्भों के साथ लोड की गई सेवा कक्षाओं के संदर्भ वाले सदस्य चर। – esaj

+0

मैंने इस बारे में सोचा था, हालांकि क्षेत्र अंतिम है। :-( –

2

कॉल को संरक्षित विधि में ले जाएं और परीक्षण में इसे ओवरराइड करें। यह आपको परीक्षण के दौरान कुछ भी वापस करने की अनुमति देता है।

+0

मैं विरासत कोड नहीं बदल सकता। मैंने अपना प्रश्न अपडेट कर लिया है। –

+0

उस स्थिति में, 'पावरमोक' आपकी सबसे अच्छी शर्त है। बस स्थिर विधि कॉल को हमेशा लपेटने के लिए भविष्य में याद रखें; वे आकर्षक हैं लेकिन परीक्षण करने के लिए कोड कठिन या असंभव बनाते हैं। –

5

ServiceLoader.load प्रलेखन से:

, दिए गए सेवा प्रकार के लिए एक नई सेवा लोडर बनाता वर्तमान धागा के संदर्भ वर्ग लोडर का उपयोग कर।

तो आप टेस्ट रन के दौरान एक विशेष संदर्भ वर्ग लोडर का उपयोग कर सकते हैं जो META-INF/service में गतिशील रूप से प्रदाता-कॉन्फ़िगरेशन फ़ाइलों को उत्पन्न करेगा। संदर्भ वर्ग लोडर ServiceLoader दस्तावेज में इस नोट के कारण प्रदाता-विन्यास फाइल के लिए खोज के लिए उपयोग किया जाएगा:

हैं कि प्रदाता लोड हो रहा है के लिए प्रयोग किया जाता है एक वर्ग लोडर के वर्ग पथ दूरदराज के नेटवर्क URL शामिल हैं तो उन URL को में प्रदाता-कॉन्फ़िगरेशन फ़ाइलों की खोज करने की प्रक्रिया में संदर्भित किया जाएगा।

संदर्भ वर्ग लोडर को सर्विस क्लास के नकली कार्यान्वयन को भी लोड करने की आवश्यकता है, जिसे बाद में नकली कार्यान्वयन के रूप में पारित किया जाता है। गतिशील तरीकों

  • गतिशील रूप से एक वर्ग (उदाहरण के ASM library प्रयोग करने के लिए) उत्पन्न अनुरोध पर getResource* प्रति अनुरोध पर प्रदाता विन्यास फाइल पैदा

    • :

      इस तरह के एक संदर्भ वर्ग लोडर दो काम करने की आवश्यकता होगी loadClass विधियों, यदि यह कक्षा है जो गतिशील रूप से जेनरेट प्रदाता कॉन्फ़िगरेशन फ़ाइल

    में निर्दिष्ट किया गया था

    उपरोक्त दृष्टिकोण का उपयोग करके, आपको मौजूदा कोड बदलने की आवश्यकता नहीं है।

  • +0

    यह काफी भारी लगता है। मैं इस तरह के एक वर्ग लोडर कैसे बनाऊंगा? –

    0

    सेवा आमतौर पर रनटाइम पर बदला जा सकता है।

    आप OSGi का उपयोग कर आप @BeforeClass साथ एनोटेट एक सेट अप विधि में सेवा कार्यान्वयन की जगह और एक @AfterClass विधि में मज़ाक उड़ाया कार्यान्वयन अपंजीकृत कर सकते हैं कर रहे हैं:

    private ServiceRegistration m_registration; 
    
    @BeforeClass 
    public void setUp() { 
        SomeInterface mockedService = Mockito.mock(SomeInterface.class); 
        m_registration = registerService(Activator.getDefault().getBundle(), Integer.MAX_VALUE, SomeInterface.class, mockedService); 
    } 
    
    @AfterClass 
    public void tearDown() { 
        if (m_registration != null) { 
        unregisterService(m_registration); 
        } 
    } 
    
    public static ServiceRegistration registerService(Bundle bundle, int ranking, Class<? extends IService> serviceInterface, Object service) { 
        Hashtable<String, Object> initParams = new Hashtable<String, Object>(); 
        initParams.put(Constants.SERVICE_RANKING, ranking); 
        return bundle.getBundleContext().registerService(serviceInterface.getName(), service, initParams); 
    } 
    
    public static void unregisterService(ServiceRegistration registration) { 
        registration.unregister(); 
    } 
    
    +1

    ओएसजीआई! = सर्विसलोडर बिल्कुल। – bmargulies

    संबंधित मुद्दे

     संबंधित मुद्दे