2013-04-26 8 views
40

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

जुनीट पैरामीटर परीक्षणों का उपयोग करके मैं सिंगल, दोगुनी, परिपत्र इत्यादि जैसे विभिन्न कार्यान्वयन की आपूर्ति कर सकता हूं लेकिन प्रत्येक कार्यान्वयन के लिए कक्षा में सभी परीक्षणों को निष्पादित करने के लिए एक ही ऑब्जेक्ट का उपयोग किया जाता है।

+0

आपका क्या मतलब है "सभी ऑब्जेक्ट्स को निष्पादित करने के लिए एक ही ऑब्जेक्ट का उपयोग किया जाता है"? –

+0

पूर्व जूनिट व्यसन के रूप में, मैं बस इतना कहना चाहूंगा कि आपको ग्रोवी/स्पॉक देखना चाहिए। स्पॉक शांत है और ग्रोवी आपको कुछ क्षमताओं देता है जो आप जूनिट के साथ नहीं कर सकते हैं। मेरी पसंदीदा चीजों में से एक निजी डेटा सदस्यों तक पहुंच रहा है, इसलिए आपको उचित इकाई परीक्षण बनाने के लिए कुछ भी बेनकाब करने की ज़रूरत नहीं है। – Thom

उत्तर

31
JUnit 4.0+ आप उपयोग कर सकते parameterized tests साथ

:,

  • अपने परीक्षण स्थिरता के लिए @RunWith(value = Parameterized.class) टिप्पणी जोड़ें कि
  • Collection लौटने एक public static पद्धति बनाएं @Parameters साथ उस पर टिप्पणी करें, और SinglyLinkedList.class, DoublyLinkedList.class, CircularList.class, आदि डाल उस संग्रह में
  • अपने परीक्षण स्थिरता में एक कन्स्ट्रक्टर जोड़ें जो Class: public MyListTest(Class cl) लेता है, औरस्टोर करता है एक उदाहरण चर listClass
  • setUp विधि या @Before में, का उपयोग List testList = (List)listClass.newInstance();

जगह में ऊपर सेटअप के साथ, पैरामिट्रीकृत धावक प्रत्येक उपवर्ग है कि आप प्रदान के लिए अपने परीक्षण स्थिरता MyListTest का एक नया उदाहरण कर देगा @Parameters विधि में, आपको परीक्षण करने के लिए आवश्यक प्रत्येक सबक्लास के लिए एक ही परीक्षण तर्क का प्रयोग करने दें।

+0

अरे! मैंने इसके बारे में क्यों नहीं सोचा। धन्यवाद। – ChrisOdney

+0

सूची परीक्षण सूची के बारे में कैसे करें (सूची) listClass.newInstance(); प्रत्येक परीक्षण विधि के बजाय सेटअप विधि में? – ChrisOdney

+0

@ChrisOdney हां, यह भी काम करेगा। यदि आपके कुछ परीक्षण विधियों को सूची वर्ग के कई उदाहरण बनाना चाहिए तो आप एक सहायक 'मेकलिस्ट()' विधि भी बना सकते हैं। – dasblinkenlight

42

मैं शायद JUnit के पैरामिट्रीकृत परीक्षण (जो IMHO सुंदर अनाड़ीपन से लागू किया जाता है) से बचने चाहते हैं, और सिर्फ एक अमूर्त List परीक्षण वर्ग जो परीक्षण कार्यान्वयन द्वारा विरासत में मिला जा सकता है बनाने:

public abstract class ListTestBase<T extends List> { 

    private T instance; 

    protected abstract T createInstance(); 

    @Before 
    public void setUp() { 
     instance = createInstance(); 
    } 

    @Test 
    public void testOneThing(){ /* ... */ } 

    @Test 
    public void testAnotherThing(){ /* ... */ } 

} 

विभिन्न कार्यान्वयन तो मिल उनके खुद ठोस वर्ग:

class SinglyLinkedListTest extends ListTestBase<SinglyLinkedList> { 

    @Override 
    protected SinglyLinkedList createInstance(){ 
     return new SinglyLinkedList(); 
    } 

} 

class DoublyLinkedListTest extends ListTestBase<DoublyLinkedList> { 

    @Override 
    protected DoublyLinkedList createInstance(){ 
     return new DoublyLinkedList(); 
    } 

} 

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

+2

उत्तर के लिए धन्यवाद, जूनिट पैरामीटर परीक्षण से इसकी अधिक सुरुचिपूर्ण है और मैं शायद इसका उपयोग कर रहा हूं। लेकिन मुझे दासब्लिंकलाइट के जवाब से चिपकना है क्योंकि मैं जूनिट के पैरामीटरेटेड टेस्ट – ChrisOdney

+1

का उपयोग करके एक रास्ता तलाश रहा था, आप इसे इस तरह से कर सकते हैं, या पैरामीटर परीक्षण का उपयोग कर सकते हैं और Assume का उपयोग कर सकते हैं। यदि कोई परीक्षण विधि है जो केवल एक (या अधिक) विशेष कक्षा पर लागू होती है, तो आप परीक्षण की शुरुआत में मान लेंगे, और यह परीक्षण अन्य वर्गों के लिए अनदेखा कर दिया जाएगा। –

+0

मुझे लगता है कि यह एक महान समाधान के लिए आधार है। किसी ऑब्जेक्ट द्वारा लागू सभी इंटरफ़ेस विधियों का परीक्षण करने में सक्षम होना महत्वपूर्ण है। लेकिन saveUser (उपयोगकर्ता) और findUserById (id) जैसी विधियों के साथ एक रिपोजिटरी गेटवे इंटरफ़ेस की कल्पना करें, जिसे विभिन्न डेटाबेस के लिए कार्यान्वित किया जाना चाहिए। FindUserById (आईडी) के लिए, testmethod विशिष्ट सेटअप को उपयोगकर्ता के साथ विशिष्ट डेटाबेस को पॉप्युलेट करने की आवश्यकता होगी। SaveUser (उपयोगकर्ता) के लिए, जोर देने वाले भाग को विशिष्ट डेटाबेस से डेटा पुनर्प्राप्त करना चाहिए। क्या यह विशिष्ट टेस्टक्लास द्वारा लागू testmethod में एक हुक (जैसे readyFindUser) जोड़कर हल किया जा सकता है? –

0

आप वास्तव में अपनी परीक्षा कक्षा में एक सहायक विधि बना सकते हैं जो आपके परीक्षण List को एक तर्क पर निर्भर आपके कार्यान्वयन का एक उदाहरण होने के लिए सेट करता है। this के साथ संयोजन में आपको इच्छित व्यवहार प्राप्त करने में सक्षम होना चाहिए।

0

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

@RunWith(value=Parameterized.class) 
public class FilterTests { 
@Parameters 
public static Collection<PluginInfo[]> getPlugins() { 
    List<PluginInfo> possibleClasses=PluginManager.getPluginsNamed("Filter"); 
    return wrapCollection(possibleClasses); 
} 
final protected PluginInfo pluginId; 
final IOPlugin CFilter; 
public FilterTests(final PluginInfo pluginToUse) { 
    System.out.println("Using Plugin:"+pluginToUse); 
    pluginId=pluginToUse; // save plugin settings 
    CFilter=PluginManager.getPluginIO(pluginId); // create an instance using the factory 
} 
//.... the tests to run 

नोट यह महत्वपूर्ण है वास्तविक पैरामीटर निर्माता को खिलाया की सरणियों का एक संग्रह के रूप में संग्रह करने के लिए, इस मामले एक वर्ग PluginInfo कहा जाता है में (मैं व्यक्तिगत रूप से पता नहीं क्यों यह इस तरह से काम करता है)। WrapCollection स्थिर फ़ंक्शन इस कार्य को निष्पादित करता है।

/** 
* Wrap a collection into a collection of arrays which is useful for parameterization in junit testing 
* @param inCollection input collection 
* @return wrapped collection 
*/ 
public static <T> Collection<T[]> wrapCollection(Collection<T> inCollection) { 
    final List<T[]> out=new ArrayList<T[]>(); 
    for(T curObj : inCollection) { 
     T[] arr = (T[])new Object[1]; 
     arr[0]=curObj; 
     out.add(arr); 
    } 
    return out; 
} 
1

@dasblinkenlight का anwser और this anwser मैं अपने उपयोग के मामले के लिए एक कार्यान्वयन है कि मैं साझा करना चाहते हैं के साथ आया था के आधार पर।

मैं इंटरफेस IImporterService को लागू करने वाले वर्गों के लिए ServiceProviderPattern (difference API and SPI) का उपयोग करता हूं। यदि इंटरफेस का एक नया कार्यान्वयन विकसित किया गया है, तो मेटा-आईएनएफ/सेवाओं/ में केवल एक कॉन्फ़िगरेशन फ़ाइल को कार्यान्वयन के लिए बदलने की आवश्यकता है।

में फ़ाइल मेटा-आईएनएफ/सेवाएं/ का नाम सेवा इंटरफ़ेस (IImporterService) के पूर्ण योग्य क्लास नाम के नाम पर रखा गया है, उदाहरण के लिए

de.myapp.importer.IImporterService

इस फ़ाइल Casses कि IImporterService, उदा लागू की एक सूची है

de.myapp.importer.impl.OfficeOpenXMLImporter

कारखाने वर्ग ImporterFactory इंटरफ़ेस का ठोस कार्यान्वयन के साथ ग्राहकों को प्रदान करता है।


ImporterFactory रिटर्न अंतरफलक के सभी कार्यान्वयन, ServiceProviderPattern के माध्यम से पंजीकृत की एक सूची। setUp() विधि यह सुनिश्चित करता है कि प्रत्येक परीक्षण मामले के लिए एक नया उदाहरण उपयोग किया जाए।

@RunWith(Parameterized.class) 
public class IImporterServiceTest { 
    public IImporterService service; 

    public IImporterServiceTest(IImporterService service) { 
     this.service = service; 
    } 

    @Parameters 
    public static List<IImporterService> instancesToTest() { 
     return ImporterFactory.INSTANCE.getImplementations(); 
    } 

    @Before 
    public void setUp() throws Exception { 
     this.service = this.service.getClass().newInstance(); 
    } 

    @Test 
    public void testRead() { 
    } 
} 

ImporterFactory.INSTANCE.getImplementations() विधि की तरह लग रहा है:

public List<IImporterService> getImplementations() { 
    return (List<IImporterService>) GenericServiceLoader.INSTANCE.locateAll(IImporterService.class); 
} 
3

मैं जानता हूँ कि यह पुराना है, लेकिन मैं एक अलग भिन्नता जो अच्छी तरह से काम करता है में यह करने के लिए जिसमें आप एक के लिए आवेदन कर सकते हैं @Parameter सीखा मूल्यों को इंजेक्ट करने के लिए फ़ील्ड सदस्य।

यह मेरी राय में थोड़ा सा क्लीनर है।

@RunWith(Parameterized.class) 
public class MyTest{ 

    private ThingToTest subject; 

    @Parameter 
    public Class clazz; 

    @Parameters(name = "{index}: Impl Class: {0}") 
    public static Collection classes(){ 
     List<Object[]> implementations = new ArrayList<>(); 
     implementations.add(new Object[]{ImplementationOne.class}); 
     implementations.add(new Object[]{ImplementationTwo.class}); 

     return implementations; 
    } 

    @Before 
    public void setUp() throws Exception { 
     subject = (ThingToTest) clazz.getConstructor().newInstance(); 
    } 
संबंधित मुद्दे