2017-06-18 44 views
5

Đã thêm @VisibleForTesting và được bảo vệ. thử nghiệm của tôi bây giờ có thể phương pháp này:Kiểm tra một lớp Phân đoạn trong sự cô lập bằng cách sử dụng Mockito

@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); 
    } 

Cập nhật các trường hợp thử nghiệm sử dụng đối tượng gián điệp: Tuy nhiên, setupDataBinding thực (recipe) đang nhận được gọi là ngay cả khi tôi đã tạo ra một mô hình của ông do thám mà sẽ được gọi. Có lẽ tôi đang làm điều này sai.

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

    fragment.displayRecipeData(recipe); 

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

Tôi đang cố gắng kiểm tra các phương thức trong lớp học Fragment như sau. Tuy nhiên, tôi đang cố gắng để thử ra các phương pháp để xác minh rằng các phương pháp được gọi là số lần chính xác. Tuy nhiên, vấn đề là tôi có phương thức privatesetupDataBinding(...) được thiết lập trên RecyclerView được gọi từ displayRecipeData(...). Tôi muốn thử các cuộc gọi này vì tôi không muốn gọi đối tượng thực sự trên RecyclerView. Tôi chỉ muốn xác minh rằng setupDataBinding(...) được gọi.

Tôi đã thử sử dụng gián điệp và VisibleForTesting, nhưng vẫn không chắc chắn cách thực hiện việc này.

Tôi đang cố gắng kiểm tra Phân đoạn một cách riêng biệt.

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(); 
    } 
} 

Đây là cách tôi đang thử nghiệm. Tôi đã thêm tư tưởng VisibleForTesting tôi có thể trợ giúp. Và tôi đã thử dùng điệp viên.

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(); 
    } 
} 

Điều gì sẽ là cách tốt nhất để kiểm tra trên trong sự cô lập?

Rất cám ơn mọi lời khuyên.

+0

Thêm '@ VisibleForTesting' là không đủ. Bạn cũng phải thay đổi công cụ sửa đổi truy cập cho 'setupDataBinding (...)' thành được bảo vệ, gói riêng tư hoặc công khai. – liminal

+0

@liminal Tôi đã cập nhật câu hỏi của mình với những trang phục mới nhất của tôi. Tôi không ngăn chặn được phương pháp thực sự từ bing được gọi là mặc dù tôi đã tạo ra một đối tượng gián điệp của nó. – ant2009

Trả lời

4

để ngăn chặn phương pháp thực được gọi là sử dụng: Mockito.doNothing().when(spy).onRecipeItem();

ở đây bạn có mẫu tối thiểu làm thế nào để sử dụng nó:

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

Tôi muốn thử những cuộc gọi như tôi don không muốn gọi đối tượng thực sự trên RecyclerView. Tôi chỉ muốn xác minh rằng setupDataBinding() được gọi.

Bạn chưa tạo đủ đường nối để thực hiện điều đó.

Điều gì xảy ra nếu bạn khai báo hợp đồng, mô tả cách "ràng buộc dữ liệu thiết lập" sẽ xảy ra? Nói cách khác, nếu bạn tạo một giao diện với phương thức void setupDataBinding(...) thì sao? Sau đó, RecipeListView sẽ giữ một phiên bản của giao diện đó dưới dạng phụ thuộc. Do đó, RecipeListView sẽ không bao giờ biết chính xác thiết lập này sẽ diễn ra như thế nào: một điều mà nó biết - sự phụ thuộc mà anh ta nắm giữ đã "ký hợp đồng" và chịu trách nhiệm thực hiện công việc.

Thông thường, bạn sẽ vượt qua mà phụ thuộc qua constructor, nhưng vì Fragment is a specific case, bạn có thể có được phụ thuộc vào 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(); 
    } 
} 

gì thực hiện điều này cho các ngươi? Bây giờ bạn có một đường may .Bây giờ bạn đang không phải phụ thuộc vào việc triển khai, bạn phụ thuộc vào hợp đồng.

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)) 
    } 
} 

tôi sẽ tư vấn mạnh "Writing Testable Code" cuốn sách Misko Hevery 's, mà minh họa tất cả các kỹ thuật với các ví dụ và theo cách ngắn gọn (38 trang).

1

Có một quy tắc chung cho ngón tay cái là: Tốt hơn hết là kiểm tra những gì đơn vị thực hiện thay vì nó hoạt động như thế nào.

Tính đến điều này, hãy tự hỏi mình một câu hỏi - tại sao tôi thử phương pháp setupDataBinding ở nơi đầu tiên? Nó không thực hiện bất kỳ cuộc gọi bên ngoài nào, nó chỉ thay đổi trạng thái của đối tượng. Vì vậy, một cách tốt hơn để kiểm tra mã này là bằng cách kiểm tra xem nó thay đổi trạng thái theo một cách chính xác:

@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()); 
} 

này có thể yêu cầu thêm nhiều getters/setters để làm cho toàn bộ điều kiểm chứng nhiều hơn một chút.

+0

Misko Hevery: "Thông thường, chú thích @VisibleForTesting là một mùi mà lớp học không được viết là dễ dàng kiểm tra. Và mặc dù nó sẽ cho phép bạn thiết lập danh sách các cuộc gọi, nó chỉ là một hack để có được khoảng gốc vấn đề." – azizbekian

+0

Vâng tôi đồng ý. Vâng, sau đó getters và setters shold là đủ. Tôi đã cập nhật câu trả lời của mình. –

Các vấn đề liên quan