2009-05-18 11 views
21

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

googling के बाद मैं 2 विचारों के साथ आया था:

  1. उपयोग EasyMock, मजाक अनुक्रम looooong लिखें। बहुत खराब समाधान: प्रारंभिक डेटा जोड़ने, डेटा बदलने के लिए मुश्किल, बड़े परीक्षण डीबगिंग संकेतों को जोड़ना मुश्किल है।
  2. मेमोरी डीबी बनाने के लिए अपाचे डर्बी या एचएसक्यूएलडीबी का उपयोग करें, इसे फ़ाइल या स्ट्रिंग सरणी से भरें, कुछ जादुई InMemoryDBUtils.query (SQL) के साथ क्वेरी करें। फिर उस परिणामसेट का उपयोग करें। दुर्भाग्यवश, मुझे टेस्ट फास्ट लिखने के लिए कोई जादुई InMemoryDBUtil नहीं मिला :-)। आईबीएम लेख "डर्बी के साथ दृढ़ता से पृथक इकाई परीक्षण" मुझे लगता है कि मुझे क्या चाहिए, हालांकि ...

दूसरा दृष्टिकोण कुछ हद तक आसान और अधिक सहायक लगता है।

ऐसी नकली बनाने के लिए आप क्या सलाह देंगे? (डॉक्टरों के बावजूद ,-)? क्या मुझे एक भौं कुछ रजत बुलेट गायब है? संभवतः, डीबीयूनीट इसके लिए उपकरण है?

उत्तर

11

डीबीयूनीट मेरे ज्ञान के लिए परिणाम सेट प्रस्तुत नहीं करता है, हालांकि यह आपको मेमोरी डेटाबेस में आपकी मदद करने में मदद करेगा।

मैं कहूंगा कि इस बिंदु पर एक मॉकिंग फ्रेमवर्क गलत दृष्टिकोण है। मॉकिंग परीक्षण व्यवहार और बातचीत के बारे में है, न केवल डेटा लौटाना, इसलिए यह आपके रास्ते में आ जाएगा।

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

आप उस कक्षा को डीबीयूनीट के साथ बैक अप ले सकते हैं, जहां आप अपने परिणाम सेट का स्नैपशॉट डबुनिट के साथ लेते हैं, और डीबीनिट ने इसे एक्सएमएल से परीक्षण के दौरान वापस पढ़ा है, और आपके डमी परिणाम ने डबुनिट के वर्गों से डेटा को पढ़ा है। यदि डेटा हल्के जटिल था तो यह एक उचित दृष्टिकोण होगा।

कक्षाओं में स्मृति के लिए जाना होगा यदि वर्ग इतने युग्मित थे कि उन्हें उसी डेटा के हिस्से के रूप में संशोधित डेटा को पढ़ने की आवश्यकता है। फिर भी, जब तक आप उस निर्भरता को अलग करने में कामयाब नहीं हो जाते, तब तक मैं वास्तविक डेटाबेस की एक प्रति का उपयोग करने पर विचार करता हूं।

एक साधारण प्रॉक्सी पीढ़ी विधि:

private static class SimpleInvocationHandler implements InvocationHandler { 
    private Object invokee; 

    public SimpleInvocationHandler(Object invokee) { 
     this.invokee = invokee; 
    } 

    public Object invoke(Object proxy, Method method, Object[] args) 
      throws Throwable { 
     method = invokee.getClass().getMethod(method.getName(), method.getParameterTypes()); 
     if (!method.isAccessible()) { 
      method.setAccessible(true); 
     } 
     try { 
      return method.invoke(invokee, args); 
     } catch (InvocationTargetException e) { 
      throw e.getTargetException(); 
     } 
    } 
} 

public static <T> T generateProxy(Object realObject, Class... interfaces) { 
    return (T) Proxy.newProxyInstance(realObject.getClass().getClassLoader(), interfaces, new SimpleInvocationHandler(realObject)); 
} 
+0

हैलो यिशई, प्रतिक्रिया के लिए धन्यवाद। 10 क्षेत्रों में से प्रत्येक के रिकॉर्ड के साथ, मैं 10 या अधिक परीक्षणों में से प्रत्येक के लिए लगभग 100 रिकॉर्ड मजाक करूँगा। यह, AFAIU, मुझे डेटा और कस्टम परिणामसेट कार्यान्वयन में डेटा स्टोर करने के लिए डीबीयूनीट के साथ छोड़ देता है। उत्तर देने के लिए फिर से धन्यवाद। – DiaWorD

+0

आपका स्वागत है। मैं कहूंगा कि यदि 10 परीक्षणों में से प्रत्येक को अलग-अलग डेटा की आवश्यकता होती है, तो हाँ, यह एक उचित दृष्टिकोण होगा। डीबीयूनीट के साथ आप एक परिणामसेट ले सकते हैं और इसे एक्सएमएल में लिख सकते हैं, इसलिए आप केवल परीक्षण में संदर्भित करते हैं। – Yishai

2

यदि लागू हो, तो आप अपने वास्तविक डेटा स्रोत से परिणाम परिणाम ले सकते हैं, इसे क्रमबद्ध कर सकते हैं और फ़ाइल को सहेज सकते हैं। फिर आप अपने प्रत्येक यूनिट परीक्षण के लिए निर्धारित परिणाम निर्धारित कर सकते हैं, और आपको जाने के लिए अच्छा होना चाहिए।

+0

हैलो GWLlosa, और आपकी प्रतिक्रिया के लिए भी धन्यवाद। मुझे लगता है कि डीबीयूनीट फाइल में डेटा संग्रहीत करने में मेरी मदद करेगा। – DiaWorD

1

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

36

मैं यहाँ से MockResultSet वर्ग के साथ सफलता मिली है: http://mockrunner.sourceforge.net/। यह आपको एक क्लास बनाने की अनुमति देता है जो परिणामसेट इंटरफ़ेस लागू करता है, और आपको प्रत्येक कॉलम और पंक्ति के लिए मान सेट करने देता है।

यदि आपकी विधियां उचित आकार के परिणामसेट्स के साथ काम कर रही हैं, तो आपको उन परीक्षणों को बनाने में सक्षम होना चाहिए जो आपको आसानी से आवश्यक मूल्यों को वापस कर दें।

MockResultSet rs = new MockResultSet("myMock"); 

rs.addColumn("columnA", new Integer[]{1}); 
rs.addColumn("columnB", new String[]{"Column B Value"}); 
rs.addColumn("columnC", new Double[]{2}); 

// make sure to move the cursor to the first row 
try 
{ 
    rs.next(); 
} 
catch (SQLException sqle) 
{ 
    fail("unable to move resultSet"); 
} 

// process the result set 
MyObject obj = processor.processResultSet(rs); 

// run your tests using the ResultSet like you normally would 
assertEquals(1, obj.getColumnAValue()); 
assertEquals("Column B Value", obj.getColumnBValue()); 
assertEquals(2.0d, obj.getColumnCValue()); 
+0

हैलो mjd79, और आपके उत्तर के लिए धन्यवाद। मेरे मामले में समस्या यह है कि मैं निश्चित रूप से प्रत्येक टेस्टकेस के लिए लगभग 100 रिकॉर्ड रखूंगा और लगभग 10 टेस्टकेस :-) प्रत्येक रिकॉर्ड में 10 फ़ील्ड होते हैं, जिनमें तिथियां, संख्याएं और स्ट्रिंग शामिल हैं। इसके कारण, मैं फ़ाइल में नकली डेटा स्टोर करना पसंद करूंगा, अधिमानतः इंसर्ट इंटो फॉर्म में, जिसे लगभग हर डीबी क्लाइंट से सीधे लिया जा सकता है। मॉकरनर बहुत अच्छा लग रहा है, लेकिन मैं इसे किसी अन्य मामले के लिए रखूंगा। आपके समय के लिए फिर से धन्यवाद। – DiaWorD

+0

नकली नौकरी परीक्षण के लिए एक साधारण परिणाम सेट का नकल करने के लिए mockrunner एक महान संसाधन था। बहुत बहुत धन्यवाद! – EdgeCaseBerg

+0

यह स्वीकार्य उत्तर होना चाहिए। – JonyD

5

Mockrunner एक CSV या XML फ़ाइल लोड और स्वचालित रूप से एक MockResultSet बना सकते हैं:

यहाँ एक सरल उदाहरण है। यह कनेक्शन और स्टेटमेंट का नकल भी कर सकता है, इसलिए आपकी सभी जेडीबीसी सामग्री बस आपके क्लासपाथ में जेडीबीसी ड्राइवर को जोड़ने के बिना काम करती है।

5

मैंने इसी मामले के लिए कुछ लिखा है। आप मॉकिटो का उपयोग कर परिणामसेट का मज़ाक उड़ा सकते हैं। कोड के इस टुकड़े के साथ परिणामet.next() को मजाक करके आप परिणामसेट की नकली पंक्तियों पर भी लूप कर सकते हैं।

// two dimensional array mocking the rows of database. 
String[][] result = { { "column1", "column2" }, { "column1", "column2" } }; 

@InjectMocks 
@Spy 
private TestableClass testableClass; 

@Mock 
private Connection connection; 

@Mock 
private Statement statement; 

@Mock 
private ResultSet resultSet; 

@BeforeTest 
public void beforeTest() { 
    MockitoAnnotations.initMocks(this); 
} 

@BeforeMethod 
public void beforeMethod() throws SQLException { 
    doAnswer(new Answer<Connection>() { 
     public Connection answer(InvocationOnMock invocation) 
       throws Throwable { 
      return connection; 

     } 
    }).when(testableClass).getConnection(); 

    when(connection.createStatement()).thenReturn(statement); 
    when(statement.executeQuery(anyString())).thenReturn(resultSet); 
    final AtomicInteger idx = new AtomicInteger(0); 
    final MockRow row = new MockRow(); 

    doAnswer(new Answer<Boolean>() { 

     @Override 
     public Boolean answer(InvocationOnMock invocation) throws Throwable { 
      int index = idx.getAndIncrement(); 
      if (result.length > index) { 
       String[] current = result[index]; 
       row.setCurrentRowData(current); 
       return true; 
      } else 
       return false; 

     } 

     ; 
    }).when(resultSet).next(); 

    doAnswer(new Answer<String>() { 

     @Override 
     public String answer(InvocationOnMock invocation) throws Throwable { 
      Object[] args = invocation.getArguments(); 
      int idx = ((Integer) args[0]).intValue(); 
      return row.getColumn(idx); 
     } 

     ; 
    }).when(resultSet).getString(anyInt()); 
} 

static class MockRow { 
    String[] rowData; 

    public void setCurrentRowData(String[] rowData) { 
     this.rowData = rowData; 
    } 

    public String getColumn(int idx) { 
     return rowData[idx - 1]; 
    } 
} 
संबंधित मुद्दे