1

I have a very simple app built using MVP pattern. This is my Contract:

public interface CitiesContract {
    interface View {
        void addCitiesToList(List<City> cityList);
    }

    interface Presenter {
        void getCityList();
    }

    interface Model {
        List<City> getCityList();
    }
}

This is my View:

public class CitiesActivity extends AppCompatActivity implements CitiesContract.View {
    private List<City> cityList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cities);

        CitiesPresenter presenter = new CitiesPresenter(this);
        presenter.getCityList();
    }

    @Override
    public void addCitiesToList(List<City> cities) {
        cityList.addAll(cities);
    }
}

This is my Presenter:

public class CitiesPresenter implements CitiesContract.Presenter {
    private CitiesContract.View view;
    private CitiesModel model;

    public CitiesPresenter(CitiesContract.View view) {
        this.view = view;
        model = new CitiesModel();
    }

    @Override
    public void getCityList() {
        List<City> cityList = model.getCityList();
        view.addCitiesToList(cityList);
    }
}

This is my Model:

public class CitiesModel implements CitiesContract.Model {
    @Override
    public List<City> getCityList() {
        List<City> cityList = new ArrayList<>();
        //Add 30 cities to the list
        return cityList;
    }
}

How can I test the getCityList() method within my Presenter? This is what I have tried so far:

public class CitiesPresenterTest {
    private CitiesContract.Presenter citiesPresenter;
    @Mock
    private CitiesContract.View citiesView;

    public void setUp() {
        MockitoAnnotations.initMocks(this);
        citiesPresenter = new CitiesPresenter(citiesView);
    }

    @Test
    public void getCityListTest() {
        citiesPresenter.getCityList();
        //What to do next???
    }
}
Mee Noi
  • 99
  • 1
  • 8
  • *What to do next???* first change name of this method to be more relevant to what it is doing ... then check if it does it – Selvin Jun 25 '19 at 11:26
  • @Selvin Sorry my bad, it is `getCityListTest()`. Just updated my question. What do you mean check if it does it? – Mee Noi Jun 25 '19 at 11:30
  • 1
    I meant this method `interface Presenter { void getCityList(); }` ... it's definitely should not called *getSomthing* ... as you can not get something with it – Selvin Jun 25 '19 at 11:31
  • @Selvin What would be a more appropriate name? – Mee Noi Jun 25 '19 at 11:32

1 Answers1

1

Ok, that's a good question btw. First of all you also need to mock you model. Second: arrange something: for example that model.getCityList() return null. After that you can verify using mockitos verify operator. Example:

when(model.getCityList()).thenReturn(null);
citiesPresenter.getCityList();
verify(view).addCitiesToList(null);

Another case can be just like that, but with an empty list:

List<City> citiesList = new ArrayList<>();

when(model.getCityList()).thenReturn(citiesList );
citiesPresenter.getCityList();
verify(view).addCitiesToList(citiesList);

And another one can be just like that, with a fake List you can build on your own just to test it:

List<City> citiesList = new ArrayList<>();
list.add(City("name", "something else", "i don't know what atributes you have"));

when(model.getCityList()).thenReturn(citiesList );
citiesPresenter.getCityList();
verify(view).addCitiesToList(citiesList);

Hope I helped.

Additional information: When unit testing you should have 3 basic steps in your head: First you Arrange: So you create your own scenario.Example what if list is null. Second: you Act: this step is where you test the method you want. Third: Assert: this is where you verify or assert that your expectations match with given code.

Mee Noi
  • 99
  • 1
  • 8
coroutineDispatcher
  • 7,718
  • 6
  • 30
  • 58
  • Hi and thanks for answering my question. I'll definitely try that and get back to you. Do you also think that I should change the name of `interface Presenter { void getCityList(); }` as also Selvin mentioned in his comment? If yes, what would be more appropriate name? – Mee Noi Jun 25 '19 at 11:48
  • 1
    Yes, I do. When you call a method with the `get` keyword prefix, you are saying that that method returns something. That name for example should be `handleCityList` or something. Remember to call your methods only for what they do, not for what they are. – coroutineDispatcher Jun 25 '19 at 11:50
  • Thank you again for that. I'll try to implement your solution and get back to you. – Mee Noi Jun 25 '19 at 11:52
  • Don't forget to mock the model also – coroutineDispatcher Jun 25 '19 at 11:53
  • Voted-up for your effort. You are using `when(model).getCityList().thanReturn(null);`From where can I get the model? This is what it ment to mock the `model` object? Can you please provide me an example? – Mee Noi Jun 25 '19 at 12:02
  • yes, just mock the model just as you have mocked the `citiesView`. identicall – coroutineDispatcher Jun 25 '19 at 12:02
  • I have added as you said: `@Mock private CitiesContract.Model citiesModel;` and then instantiate it in `setUp()` method `citiesModel = new CitiesModel();` and finally used as you specified in your answer: `when(citiesModel).getCityList().thanReturn(null);` but I get: `Cannot resolve method 'getCityList()'`. On `when(citiesModel)` I can only call `.getMock()`, nothing else. What's the issue? – Mee Noi Jun 25 '19 at 12:12
  • No you are mocking the wrong model. here: `@Mock CitiesModel model` and that's all you have to do. – coroutineDispatcher Jun 25 '19 at 12:15
  • I have changed the type of the object from `CitiesContract.Model` to `CitiesModel` and I still have the same error. `when(citiesModel)` return an object of type `OngoingStubbing` and I cannot call `.getCityList()` on it. – Mee Noi Jun 25 '19 at 12:22
  • drop the model instantiation from the constructor in production code – coroutineDispatcher Jun 25 '19 at 12:24
  • I moved the model instantiation the from `setUp()` to `handleCityList()` and I have the same error. I cannot call `.getCityList()` on an object of type `OngoingStubbing`. – Mee Noi Jun 25 '19 at 12:27
  • omg I'm so sorry I must have confused you. You should do it like this: `when(cityModel.getCityList()).thenReturn(null)` – coroutineDispatcher Jun 25 '19 at 12:27
  • No error now but I get `java.lang.NullPointerException` that is pointing to this line: `when(citiesModel.getCityList()).thenReturn(null);` Is this the correct behaviour? – Mee Noi Jun 25 '19 at 12:31
  • ok your presenter is not well constructed. I believe you must include the model in the constructor in order to mock it. just like the `view` you have used. – coroutineDispatcher Jun 25 '19 at 12:35
  • I should include the model in the constructor of which class? – Mee Noi Jun 25 '19 at 12:37
  • in the presenter: `CitiesPresenter(CitiesContract.View view, CitiesModel model)` and then use mockito in the test just like I have described – coroutineDispatcher Jun 25 '19 at 12:39
  • Hmm, I don't understand. If you say so, it means that when I create an object of the Presenter class in my activity, beside the view I also should pass the model in the constructor. The logic of using the model shouldn't be only in the Presenter and not in the activity? – Mee Noi Jun 25 '19 at 12:42
  • Please take a look at this https://stackoverflow.com/questions/8995540/mocking-member-variables-of-a-class-using-mockito – coroutineDispatcher Jun 25 '19 at 12:43
  • I finally solved it. All three cases were very helpful. Thank you! – Mee Noi Jun 26 '19 at 14:53