Added @VisibleForTesting
and protected. My test can now this method:
@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);
}
Updated the test case using spy object: However, the real setupDataBinding(recipe) is getting called even when I created a mock of he spy that will get called. Maybe I am doing this 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 the methods in my Fragment
class as below. However, I am trying to mock out the methods to verify that the methods are called the correct number of times. However, the problem is I have a private
method setupDataBinding(...)
that setups on the RecyclerView
that is called from displayRecipeData(...)
. I want to mock these calls as I don't want to call the real object on the RecyclerView
. I just want to verify that setupDataBinding(...)
gets called.
I have tried using spy and VisibleForTesting
, but still not sure how to do this.
I am trying to test the 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 am testing. I have added the VisibleForTesting
thinking I could help. And I have tried using the 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 test the above in isolation?
Many thanks for any advice.
to prevent real method being called use: Mockito.doNothing().when(spy).onRecipeItem();
here you have minimum 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();
}
}
}
I want to mock these calls as I don't want to call the real object on the
RecyclerView
. I just want to verify, thatsetupDataBinding()
gets called.
You haven't created enough seams in order to perform that.
What if you declare a contract, which describes how the "setup data binding" will occur? In other words, what if you create an interface with method void setupDataBinding(...)
? Then RecipeListView
will hold an instance of that interface as a dependency. Thus, RecipeListView
won't ever know how exactly this setup will take place: one thing it knows - the dependency he holds has "signed the contract" and took the responsibility to perform the job.
Normally, you would pass that dependency via constructor, but because Fragment
is a specific case, you can acquire 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 (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();
}
}
What does this give to you? Now you have a seam. Now you are not dependent on implementation, you are dependent on a 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 advice Misko Hevery's "Writing Testable Code" book, which illustrates all the techniques with examples and in concise manner (38 pages).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With