Testing a fragment class in isolation using Mockito

Added by @VisibleForTesting and is protected. My test can now be as follows:

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

A test case using the spy object has been updated: however, the real setupDataBinding (recipe) is called even when I created the layout of its spy, which will be called. Maybe I'm doing it wrong.

 @Test public void testShouldGetAllRecipes() { RecipeListView spy = Mockito.spy(fragment); doNothing().when(spy).setupDataBinding(recipe); fragment.displayRecipeData(recipe); verify(recipeItemClickListener, times(1)).onRecipeItemClick(); } 

I am trying to test methods in my Fragment class as shown below. However, I am trying to make fun of methods to verify that the methods are called the correct number of times. However, the problem is that I have a private setupDataBinding(...) method that is configured with a RecyclerView that is called from displayRecipeData(...) . I want to mock these calls because I don’t want to name the real object on RecyclerView . I just want to check what the setupDataBinding(...) call is.

I tried using spy and VisibleForTesting , but still not sure how to do this.

I am trying to test a fragment in isolation.

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

This is how I test. I added a VisibleForTesting thinking that I could help you. And I tried to use a spy.

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

What would be the best way to verify the above?

Thanks so much for any advice.

+5
source share
3 answers

to prevent the use of the real method: Mockito.doNothing().when(spy).onRecipeItem();

here you have a minimal sample how to use it:

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

}

+4
source

I want to mock these calls as I don't want to call a real object on a RecyclerView . I just want to check what the setupDataBinding() call is.

You have not created enough seams to complete this.

What if you declare a contract that describes how the “installation data binding” will occur? In other words, what if you create an interface with the void setupDataBinding(...) method? Then the RecipeListView will contain an instance of this interface as a dependency. Thus, RecipeListView will never know exactly how this installation will take place: one thing that he knows is the dependency that he holds, “signed a contract” and took responsibility for the task.

Usually you pass this dependency through the constructor, but since Fragment is a specific case , you can get the dependencies in 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 (eg 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(); } } 

What does this give you? Now you have a seam . Now you are not dependent on the implementation, you are dependent on the contract.

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

I would strongly advise Misko Hevery "Writing Test Code" , which illustrates all the methods with examples and a concise way (38 pages).

+3
source

There is a general rule: It is much better to check what the block does, and not how it does it.

Given this, ask yourself the question - why would I make fun of the setupDataBinding method in the first place ? It does not make any external calls, it only changes the state of the object. Therefore, the best way to verify this code is to verify that the state change is correct:

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

This may require the addition of several getters / setters in order to make all this more tested.

+1
source

Source: https://habr.com/ru/post/1268945/


All Articles