2014-12-23 5 views
5

मेरे पास निम्न सरल डायनेमो डीबीडाओ है जिसमें एक विधि है जो एक इंडेक्स से पूछताछ करता है और परिणामों का नक्शा देता है।मॉकिटो: मॉकिंग पैकेज निजी कक्षाएं

import com.amazonaws.services.dynamodbv2.document.*; 

public class DynamoDBDao implements Dao{ 
    private Table table; 
    private Index regionIndex; 

    public DynamoDBDao(Table table) { 
     this.table = table; 
    } 

    @PostConstruct 
    void initialize(){ 
     this.regionIndex = table.getIndex(GSI_REGION_INDEX); 
    } 

    @Override 
    public Map<String, Long> read(String region) { 
     ItemCollection<QueryOutcome> items = regionIndex.query(ATTR_REGION, region); 
     Map<String, Long> results = new HashMap<>(); 
     for (Item item : items) { 
      String key = item.getString(PRIMARY_KEY); 
      long value = item.getLong(ATTR_VALUE); 
      results.put(key, value); 
     } 
     return results; 
    } 
} 

मैं एक इकाई परीक्षण जो पुष्टि करता है कि जब DynamoDB सूचकांक एक ItemCollection रिटर्न तो दाव रिटर्न इसी परिणाम नक्शा लिखने की कोशिश कर रहा हूँ।

public class DynamoDBDaoTest { 

    private String key1 = "key1"; 
    private String key2 = "key2"; 
    private String key3 = "key3"; 
    private Long value1 = 1l; 
    private Long value2 = 2l; 
    private Long value3 = 3l; 

    @InjectMocks 
    private DynamoDBDao dynamoDBDao; 

    @Mock 
    private Table table; 

    @Mock 
    private Index regionIndex; 

    @Mock 
    ItemCollection<QueryOutcome> items; 

    @Mock 
    Iterator iterator; 

    @Mock 
    private Item item1; 

    @Mock 
    private Item item2; 

    @Mock 
    private Item item3; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     when(table.getIndex(DaoDynamo.GSI_REGION_INDEX)).thenReturn(regionIndex); 
     dynamoDBDao.initialize(); 

     when(item1.getString(anyString())).thenReturn(key1); 
     when(item1.getLong(anyString())).thenReturn(value1); 
     when(item2.getString(anyString())).thenReturn(key2); 
     when(item2.getLong(anyString())).thenReturn(value2); 
     when(item3.getString(anyString())).thenReturn(key3); 
     when(item3.getLong(anyString())).thenReturn(value3); 
    } 

    @Test 
    public void shouldReturnResultsMapWhenQueryReturnsItemCollection(){ 

     when(regionIndex.query(anyString(), anyString())).thenReturn(items); 
     when(items.iterator()).thenReturn(iterator); 
     when(iterator.hasNext()) 
       .thenReturn(true) 
       .thenReturn(true) 
       .thenReturn(true) 
       .thenReturn(false); 
     when(iterator.next()) 
       .thenReturn(item1) 
       .thenReturn(item2) 
       .thenReturn(item3); 

     Map<String, Long> results = soaDynamoDbDao.readAll("region"); 

     assertThat(results.size(), is(3)); 
     assertThat(results.get(key1), is(value1)); 
     assertThat(results.get(key2), is(value2)); 
     assertThat(results.get(key3), is(value3)); 
    } 
} 

मेरे समस्या यह है कि items.iterator() वास्तव में वापस नहीं करता है इटरेटर यह एक IteratorSupport जो DynamoDB दस्तावेज़ एपीआई में एक पैकेज निजी वर्ग है देता है। इसका मतलब यह है कि मैं वास्तव में इसे ऊपर नहीं कर सकता जैसा मैंने ऊपर किया था और इसलिए मैं अपना बाकी परीक्षण पूरा नहीं कर सकता।

इस मामले में मैं क्या कर सकता हूं? मैं डायनामो डीबी दस्तावेज़ एपीआई में इस भयानक पैकेज निजी कक्षा को सही ढंग से मेरे डीएओ का परीक्षण कैसे करूं?

+1

क्रियान्वयन विवरण दिशानिर्देश के लिए कारणों में से एक है "([नहीं नकली प्रकार आपकी नहीं है करते हैं] http : //www.davesquared.net/2011/04/dont-mock-types-you-dont-own.html) "। क्या आप इन ऑब्जेक्ट्स में से किसी एक अनुबंध/कार्यान्वयन के साथ एक इंटरस्ट्रक्शन लिख सकते हैं, या इसके बजाय इंटरफेस के खिलाफ कोड? –

+0

हाय जेफ, आपकी टिप्पणी के लिए धन्यवाद। मैं नहीं देखता कि मैं इन वस्तुओं में एक अनुबंध/कार्यान्वयन के साथ नियंत्रण कैसे कर सकता हूं। मैंने अपने वर्तमान टूलसेट को समाप्त कर दिया है जो मेरे ज्ञान और अनुभव से सीमित है। क्या आप कुछ ऐसा देख सकते हैं जिसे मैं वर्तमान में नहीं कर सकता? यदि ऐसा है तो मैं आभारी रहूंगा यदि आप मुझे सही दिशा में इंगित कर सकते हैं। –

उत्तर

1

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

मुझे लगता है कि इस मामले के लिए एक बेहतर दृष्टिकोण परंपरागत तरीके से जाने के लिए और एडब्ल्यूएस टीम द्वारा DynamodbLocal पुस्तकालय उपयोग करने का प्रयास नहीं किया जाएगा - http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html

यह मूल रूप से एक DyanamoDB की स्मृति कार्यान्वयन में है। हमने अपने परीक्षण लिखे थे जैसे यूनिट टेस्ट प्रारंभिकरण के दौरान, डायनमोडबोकोकल इंस्टेंस तैयार किया जाएगा और टेबल बनाए जाएंगे। यह परीक्षण एक हवा बनाता है। हमें अभी तक लाइब्रेरी में कोई भी बग नहीं मिली है और यह एडब्ल्यूएस द्वारा सक्रिय रूप से समर्थित और विकसित है। अत्यधिक अनुशंसा करते हैं।

5

सबसे पहले, यूनिट परीक्षणों को कभी भी किसी ऑब्जेक्ट में निजी स्थिति को सत्यापित करने की कोशिश नहीं करनी चाहिए। यह बदल सकता है। यदि कक्षा गैर-निजी गेटर विधियों के माध्यम से अपने राज्य का पर्दाफाश नहीं करती है, तो यह आपके परीक्षण का कोई भी व्यवसाय नहीं है, इसे कैसे कार्यान्वित किया जाता है।

दूसरा, आप इस बात की परवाह क्यों करते हैं कि इटरेटर के कार्यान्वयन में क्या है? कक्षा ने एक इटरेटर (एक इंटरफ़ेस) लौटकर अपना अनुबंध पूरा कर लिया है, जब इसे फिर से चालू किया जाता है, तो वह ऑब्जेक्ट वापस कर देगा।

तीसरा, आप ऐसी वस्तुओं का मज़ाक क्यों कर रहे हैं जिनकी आपको आवश्यकता नहीं है? अपनी मजाकिया वस्तुओं के लिए इनपुट और आउटपुट बनाएं, उन्हें नकल न करें; यह अनावश्यक है। आप अपने कन्स्ट्रक्टर में एक टेबल पास करते हैं? ठीक।
फिर आपको जो कुछ भी चाहिए के लिए जो भी सुरक्षित तरीके बनाने के लिए टेबल क्लास का विस्तार करें। संरक्षित गेटर्स और/या सेटर्स को अपने टेबल सबक्लास में जोड़ें। यदि आवश्यक हो तो उन्हें हार्ड कोडित मान वापस कर दें। वे कोई फर्क नहीं पड़ता।

याद रखें, केवल अपनी परीक्षा कक्षा में एक कक्षा का परीक्षण करें। आप दाओ को टेबल और न ही इंडेक्स का परीक्षण कर रहे हैं।

+0

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

+0

क्या आपने अपने लूप में निहित कॉल द्वारा लौटाए गए इटरेटर को नकल करने की कोशिश की है? एक Iterable लौटने के लिए ItemCollection को ओवरराइड करें जो आपकी इच्छानुसार व्यवहार करता है। –

+0

यह मान्य जावा नहीं है। जावा रनटाइम पर प्रकार की जांच करेगा। 'Iterator' का एक मनमाना उदाहरण 'IteratorSupprt' का मान्य उदाहरण नहीं है, इसलिए जावा रनटाइम अपवाद फेंक देगा। मैंने एडब्ल्यूएस के साथ इस बारे में एक मुद्दा दायर किया और इटेटरेटर अब पैकेज सुरक्षित नहीं है: https://github.com/aws/aws-sdk-java/issues/465 – Max

0

एक संभावित समाधान, एक परीक्षण वर्ग है जो एक ही पैकेज है कि यह में मौजूद है में IteratorSupport फैली वांछित व्यवहार

इसके बाद आप अपनी नकली सेटअप के माध्यम से इस वर्ग का एक उदाहरण लौट सकते हैं परिभाषित करते हैं, और परिभाषित करने के लिए है परीक्षण का मामला।

बेशक, यह एक अच्छा समाधान नहीं है, लेकिन उसी कारण के लिए केवल एक कामकाज है कि @ जेफ बोमन ने टिप्पणी में उल्लेख किया था।

0

क्या यह अलग-अलग विधि में आइटमकोलेक्शन पुनर्प्राप्ति निकालने के लिए बेहतर होगा? अपने मामले में यह इस प्रकार है लग सकता है में:

public class DynamoDBDao { 

    protected Iterable<Item> readItems(String region) { // can be overridden/mocked in unit tests 
    // ItemCollection implements Iterable, since ItemCollection-specific methods are not used in the DAO we can return it as Iterable instance 
    return regionIndex.query(ATTR_REGION, region); 
    } 
} 

तो इकाई परीक्षण में:

private List<Item> mockItems = new ArrayList<>(); // so you can set these items in your test method 

private DynamoDBDao dao = new DynamoDBDao(table) { 
    @Override 
    protected Iterable<Item> readItems(String region) { 
    return mockItems; 
    } 
} 
0

जब आप when(items.iterator()).thenReturn(iterator); Mockito जो संकलन त्रुटि का कारण बनता है ItemCollection के रूप में आइटम देखता है का उपयोग करें। अपने परीक्षण मामले में, आप आइटम कोलेक्शन को केवल एक इटेबल के रूप में देखना चाहते हैं। तो, सरल उपाय नीचे की तरह Iterable के रूप में आइटम कास्ट करने के लिए है:

when(((Iterable<QueryOutcome>)items).iterator()).thenReturn(iterator); 

के रूप में भी

@Mock 
Iterator<QueryOutcome> iterator; 

अपने इटरेटर कर यह चेतावनी :) बिना कोड ठीक करना चाहिए

यदि यह ठीक करता है समस्या, कृपया जवाब स्वीकार करें।

0

आप इस तरह नकली वस्तुओं का उपयोग करके अपने पढ़ने विधि का परीक्षण कर सकते हैं:

दृश्यता की तरह
public class DynamoDBDaoTest { 

@Mock 
private Table table; 

@Mock 
private Index regionIndex; 


@InjectMocks 
private DynamoDBDao dynamoDBDao; 

public DynamoDBDaoTest() { 
} 

@Before 
public void setUp() { 
    MockitoAnnotations.initMocks(this); 
    when(table.getIndex(GSI_REGION_INDEX)).thenReturn(regionIndex); 
    dynamoDBDao.initialize(); 
} 

@Test 
public void shouldReturnResultsMapWhenQueryReturnsItemCollection() { 
    when(regionIndex.query(anyString(), anyString())).thenReturn(new FakeItemCollection()); 
    final Map<String, Long> results = dynamoDBDao.read("region"); 
    assertThat(results, allOf(hasEntry("key1", 1l), hasEntry("key2", 2l), hasEntry("key3", 3l))); 
} 

private static class FakeItemCollection extends ItemCollection<QueryOutcome> { 
    @Override 
    public Page<Item, QueryOutcome> firstPage() { 
     return new FakePage(); 
    } 
    @Override 
    public Integer getMaxResultSize() { 
     return null; 
    } 
} 

private static class FakePage extends Page<Item, QueryOutcome> { 
    private final static List<Item> items = new ArrayList<Item>(); 

    public FakePage() { 
     super(items, new QueryOutcome(new QueryResult())); 

     final Item item1= new Item(); 
     item1.with(PRIMARY_KEY, "key1"); 
     item1.withLong(ATTR_VALUE, 1l); 
     items.add(item1); 

     final Item item2 = new Item(); 
     item2.with(PRIMARY_KEY, "key2"); 
     item2.withLong(ATTR_VALUE, 2l); 
     items.add(item2); 

     final Item item3 = new Item(); 
     item3.with(PRIMARY_KEY, "key3"); 
     item3.withLong(ATTR_VALUE, 3l); 
     items.add(item3); 
    } 

    @Override 
    public boolean hasNextPage() { 
     return false; 
    } 

    @Override 
    public Page<Item, QueryOutcome> nextPage() { 
     return null; 
    } 
} 
संबंधित मुद्दे