2017-06-18 15 views
5

का उपयोग करके अलगाव में एक टुकड़ा वर्ग का परीक्षण @VisibleForTesting और संरक्षित। मेरा परीक्षण अब कर सकते हैं इस विधि:मॉकिटो

@VisibleForTesting 
    protected void setupDataBinding(List<Recipe> recipeList) { 
     recipeAdapter = new RecipeAdapter(recipeList); 
     RecyclerView.LayoutManager layoutManager 
       = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false); 
     rvRecipeList.setLayoutManager(layoutManager); 
     rvRecipeList.setAdapter(recipeAdapter); 
    } 

अपडेट किया गया परीक्षण का मामला जासूस वस्तु का उपयोग कर: हालांकि, वास्तविक setupDataBinding (नुस्खा) भी कहा जाता हो रही है जब मैं वह की एक नकली बनाया जासूसी कहा जाता हो जाएगा कि। शायद मैं यह गलत कर रहा हूँ।

@Test 
public void testShouldGetAllRecipes() { 
    RecipeListView spy = Mockito.spy(fragment); 
    doNothing().when(spy).setupDataBinding(recipe); 

    fragment.displayRecipeData(recipe); 

    verify(recipeItemClickListener, times(1)).onRecipeItemClick(); 
} 

मैं नीचे के रूप में मेरी Fragment कक्षा में तरीकों का परीक्षण करने की कोशिश कर रहा हूँ। हालांकि, मैं यह सत्यापित करने के तरीकों का नकल करने की कोशिश कर रहा हूं कि विधियों को सही संख्या कहा जाता है। हालांकि, समस्या यह है कि मेरे पास private विधि setupDataBinding(...) है जो RecyclerView पर सेट है जिसे displayRecipeData(...) से कहा जाता है। मैं इन कॉलों को मॉक करना चाहता हूं क्योंकि मैं RecyclerView पर असली ऑब्जेक्ट को कॉल नहीं करना चाहता हूं। मैं बस सत्यापित करना चाहता हूं कि setupDataBinding(...) कॉल हो जाता है।

मैंने जासूसी और VisibleForTesting का उपयोग करने का प्रयास किया है, लेकिन अभी भी यह सुनिश्चित नहीं है कि यह कैसे करें।

मैं अलगाव में फ्रैगमेंट का परीक्षण करने की कोशिश कर रहा हूं।

public class RecipeListView 
     extends MvpFragment<RecipeListViewContract, RecipeListPresenterImp> 
     implements RecipeListViewContract { 

    @VisibleForTesting 
    private void setupDataBinding(List<Recipe> recipeList) { 
     recipeAdapter = new RecipeAdapter(recipeList); 
     RecyclerView.LayoutManager layoutManager 
       = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false); 
     rvRecipeList.setLayoutManager(layoutManager); 
     rvRecipeList.setAdapter(recipeAdapter); 
    } 

    @Override 
    public void displayRecipeData(List<Recipe> recipeList) { 
     /* Verify this get called only once */ 
     setupDataBinding(recipeList); 

     recipeItemListener.onRecipeItem(); 
    } 
} 

इस प्रकार मैं परीक्षण कर रहा हूं। मैंने VisibleForTesting को सोचा है कि मैं मदद कर सकता हूं। और मैंने जासूसी का उपयोग करने की कोशिश की है।

public class RecipeListViewTest { 
    private RecipeListView fragment; 
    @Mock RecipeListPresenterContract presenter; 
    @Mock RecipeItemListener recipeItemListener; 
    @Mock List<Recipe> recipe; 

    @Before 
    public void setup() { 
     MockitoAnnotations.initMocks(RecipeListViewTest.this); 
     fragment = RecipeListView.newInstance(); 
    } 

    @Test 
    public void testShouldGetAllRecipes() { 
     fragment.displayRecipeData(recipe); 
     RecipeListView spy = Mockito.spy(fragment); 

     verify(recipeItemListener, times(1)).onRecipeItem(); 
    } 
} 

अलगाव में उपर्युक्त परीक्षण करने का सबसे अच्छा तरीका क्या होगा?

किसी भी सलाह के लिए बहुत धन्यवाद।

+0

'VisibleForTesting' जोड़ना पर्याप्त नहीं है। आपको संरक्षित, पैकेज-निजी या सार्वजनिक के लिए 'setupDataBinding (...)' के लिए एक्सेस संशोधक को भी बदलना होगा। – liminal

+0

@ प्रारंभिक मैंने अपने प्रश्नों को अपने नवीनतम प्रयासों के साथ अपडेट किया है। मैं असली विधि को बिंग से रोकने में असफल रहा हूं, भले ही मैंने इसका एक जासूसी वस्तु बनाया हो। – ant2009

उत्तर

4

वास्तविक विधि को रोकने के लिए उपयोग बुलाया जा रहा है: Mockito.doNothing().when(spy).onRecipeItem();

यहाँ

आप कम से कम नमूना है कि यह कैसे उपयोग करने के लिए:

public class ExampleUnitTest { 
    @Test 
    public void testSpyObject() throws Exception { 
     SpyTestObject spyTestObject = new SpyTestObject(); 
     SpyTestObject spy = Mockito.spy(spyTestObject); 

     Mockito.doNothing().when(spy).methodB(); 

     spy.methodA(); 
     Mockito.verify(spy).methodB(); 
    } 

    public class SpyTestObject { 

     public void methodA() { 
      methodB(); 
     } 
     public void methodB() { 
      throw new RuntimeException(); 
     } 
    } 

}

3

मैं मैं डॉन के रूप में इन कॉल नकली करना चाहते हैं RecyclerView पर वास्तविक वस्तु को कॉल नहीं करना चाहता हूं। मैं बस सत्यापित करना चाहता हूं कि setupDataBinding() कॉल हो जाता है।

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

क्या होगा यदि आप एक अनुबंध घोषित करते हैं, जो वर्णन करता है कि "सेटअप डेटा बाध्यकारी" कैसे होगा? दूसरे शब्दों में, यदि आप विधि void setupDataBinding(...) के साथ इंटरफेस बनाते हैं तो क्या होगा? फिर RecipeListView उस इंटरफेस का एक निर्भरता के रूप में एक उदाहरण आयोजित करेगा। इस प्रकार, RecipeListView कभी नहीं पता होगा कि यह सेटअप वास्तव में कैसे होगा: एक बात यह जानता है - वह निर्भरता है जिसने "अनुबंध पर हस्ताक्षर किए हैं" और नौकरी करने की ज़िम्मेदारी ली है।

आम तौर पर, आप निर्माता के माध्यम से है कि निर्भरता से होकर गुजरेगा, लेकिन क्योंकि Fragment is a specific case, आप onAttach() में निर्भरता प्राप्त कर सकते हैं:

interface Setupper { 
    void setupDataBinding(List<Recipe> recipes, ...); 
} 

class RecipeListView extends ... { 

    Setupper setupper; 

    @Override public void onAttach(Context context) { 
     super.onAttach(context); 

     // Better let the Dependency Injection tool (e.g. Dagger) provide the `Setupper` 
     // Or initialize it here (which is not recommended) 
     Setupper temp = ... 
     initSetupper(temp); 
    } 

    void initSetupper(Setupper setupper) { 
     this.setupper = setupper; 
    } 

    @Override 
    public void displayRecipeData(List<Recipe> recipes) { 
     // `RecipeListView` doesn't know what exactly `Setupper` does 
     // it just delegates the work 
     setupper.setupDataBinding(recipes, ...); 

     recipeItemListener.onRecipeItem(); 
    } 
} 

क्या यह आपके लिए देता है? अब आपके पास सीम है।अब आप कार्यान्वयन पर निर्भर नहीं हैं, आप एक अनुबंध पर निर्भर हैं।

public class RecipeListViewTest { 

    @Mock Setupper setupper; 
    List<Recipe> recipe = ...; // initialize, no need to mock it 
    ... 

    private RecipeListView fragment; 

    @Before 
    public void setup() { 
     MockitoAnnotations.initMocks(this); 
     fragment = new RecipeListView(); 
     fragment.initSetupper(setupper); 
    } 

    @Test 
    public void testShouldGetAllRecipes() { 
     fragment.displayRecipeData(recipes); 

     // You do not care what happens behind this call 
     // The only thing you care - is to test whether is has been executed 
     verify(setupper).setupDataBinding(recipe, ...); 
     // verify(..) is the same as verify(.., times(1)) 
    } 
} 

मैं दृढ़ता से सलाह करेंगे Misko Hevery के "Writing Testable Code" किताब है, जो उदाहरण के साथ एवं संक्षिप्त रूप (38 पृष्ठों) में सभी तकनीकों को दिखाता है।

1

अंगूठे का एक आम नियम कह रहा है: इकाई के बजाए यह क्या करता है इसके खिलाफ परीक्षण करना बेहतर होता है।

लेते हुए खाते में यह अपने आप को एक सवाल पूछ - कारण है कि मैं पहली जगह में setupDataBinding विधि नकली हैं? यह कोई बाहरी कॉल नहीं करता है, यह केवल वस्तु की स्थिति को बदलता है। इसलिए इस कोड का परीक्षण करने के लिए एक बेहतर तरीका जाँच है कि क्या यह एक सही तरीका में राज्य में परिवर्तन कर रहा है:

@Test 
public void testShouldGetAllRecipes() { 
    fragment.displayRecipeData(recipeList); 

    // Verifies whether RecipeAdapter has been initialized correctly 
    RecipeAdapter recipeAdapter = fragment.getRecipeAdapter(); 
    assertNotNull(recipeAdapter); 
    assertSame(recipeList, recipeAdapter.getRecipeList()); 

    // Verifies whethr RvRecipeList has been initialized correctly 
    RvRecipeList rvRecipeList = fragment.getRvRecipeList(); 
    assertNotNull(rvRecipeList); 
    assertNotNull(rvRecipeList.getLayoutManager()); 
    assertSame(fragment.getRecipeAdapter(), rvRecipeList.getAdapter()); 
} 

यह कई टिककर खेल जोड़ने की आवश्यकता हो सकती/setters पूरी बात में थोड़ा और अधिक परीक्षण योग्य बनाने के लिए।

+0

मिस्को हेवरी: "आमतौर पर, @VisibleForTesting एनोटेशन एक गंध है कि कक्षा को के रूप में आसानी से परीक्षण नहीं किया गया था। और भले ही यह आपको कॉल की सूची सेट करने दे, फिर भी यह लगभग एक हैक है रूट मुसीबत।" – azizbekian

+0

हाँ मैं सहमत हूं। खैर, तो गेटर्स और सेटर्स को ढकना पर्याप्त हो गया। मैंने अपना जवाब अपडेट कर लिया है। –