简体   繁体   中英

Testing an android mvp presenter

What's the best way to test my mvp presenter? I've read a ton of tutorials and each one makes me even more confused. I'm just starting to learn about testing and I figured it'd be easier to start with the presenter.

My Presenter

public class HymnPresenter implements HymnPresenterContract, HymnRepository.HymnsCallback{

    private HymnRepository hymnRepository;
    private HymnsViewContract hymnViewContract;

    public HymnPresenter() {
    }

    /*public HymnPresenter(HymnRepository repository, HymnsViewContract viewContract) {
        this.hymnViewContract = viewContract;
        this.hymnRepository = repository.attach(this);
    }*/



    @Override
    public void addHymns(ArrayList<Hymn> hymns){
        if(hymns != null && !hymns.isEmpty()) {
            hymnRepository.addHymns(hymns);
            hymnViewContract.showLoading();
        }else{
            handleError("Can not add null hymn");
        }
    }

    @Override
    public void addHymn(Hymn hymn){
        if(hymn != null) {
            hymnViewContract.showLoading();
            hymnRepository.addHymn(hymn);
        }else{
            handleError("Can not add null hymn");
        }
    }

    @Override
    public void handleSuccess() {
        hymnViewContract.success();
        hymnViewContract.hideLoading();
    }

    @Override
    public void handleError(String error) {
        hymnViewContract.error(error);
        hymnViewContract.hideLoading();
    }
} 

My Repository I'm using the firebase firestore db to store the hymns

public class HymnRepository implements HymnPresenterContract{

    private HymnsCallback hymnsCallback;
    private CollectionReference collectionReference;
    private FirebaseFirestore db;

    public HymnRepository(FirebaseFirestore database, @HymnCollection CollectionReference collection) {
        this.db = database;
        this.collectionReference = collection;
    }

    public HymnRepository attach(@NonNull HymnsCallback callback){
        this.hymnsCallback = callback;
        return this;
    }

    @Override
    public void addHymns(ArrayList<Hymn> hymns){
        WriteBatch batch = db.batch();
        for(int i = 0; i < hymns.size(); i++){
            batch.set(collectionReference.document(), hymns.get(i));
        }
        batch.commit()
                .addOnSuccessListener(documentReference -> hymnsCallback.handleSuccess())
                .addOnFailureListener(e -> hymnsCallback.handleError(e.toString()));
    }

    @Override
    public void addHymn(Hymn hymn) {
        collectionReference.add(hymn)
                .addOnSuccessListener(documentReference -> hymnsCallback.handleSuccess())
                .addOnFailureListener(e -> hymnsCallback.handleError(e.toString()));
    }

    public interface HymnsCallback {
        void handleSuccess();
        void handleError(String error);
    }
}

My presenter contract

public interface HymnPresenterContract {
    void addHymns(ArrayList<Hymn> hymns);
    void addHymn(Hymn hymn);
}

My view contract

public interface HymnsViewContract {
    void success();
    void error(String error);
}

So I managed to get it to work. Thanks a lot guys... this is what I did.

@RunWith(MockitoJUnitRunner.Silent.class)
public class HymnPresenterTest {

    @InjectMocks
    public HymnPresenter hymnPresenter;
    @Mock
    public HymnRepository hymnRepository;
    @Mock
    public HymnsViewContract hymnsViewContract;
    @Mock
    private Hymn hymn;
    ArrayList<Hymn> hymns;

    @Before
    public void setUp() throws Exception {
        hymns = new ArrayList<>();
        hymns.add(hymn);
    }

    @Test
    public void addHymn_callsRepositoryAddHymn() throws Exception {
        hymnPresenter.addHymn(hymn);
        verify(hymnRepository).addHymn(eq(hymn));
    }

    @Test
    public void addHymns_callsRepositoryAddHymns() throws Exception {
        hymnPresenter.addHymns(hymns);
        verify(hymnRepository).addHymns(eq(hymns));
    }

    @Test
    public void addHymns_callsViewContractShowLoading() throws Exception {
        hymnPresenter.addHymns(hymns);
        verify(hymnsViewContract).showLoading();
    }

    @Test
    public void addHymn_callsViewContractShowLoading() throws Exception {
        hymnPresenter.addHymn(hymn);
        verify(hymnsViewContract).showLoading();
    }

    @Test
    public void addNullHymn_callsViewContractError() throws Exception {
        hymnPresenter.addHymn(null);
        verify(hymnsViewContract).error(anyString());
        verify(hymnsViewContract).hideLoading();
    }

    @Test
    public void addEmptyHymnList_callsViewContractError() throws Exception {
        ArrayList<Hymn> list = new ArrayList<>();
        hymnPresenter.addHymns(list);
        verify(hymnsViewContract).error(anyString());
        verify(hymnsViewContract).hideLoading();
    }


    @Test
    public void addNonNullHymn_callsViewContractSuccess() throws Exception {
        doAnswer(invocation -> {hymnPresenter.handleSuccess();
        return null;}).when(hymnRepository).addHymn(any(Hymn.class));

        hymnPresenter.addHymn(hymn);
        verify(hymnsViewContract).success();
        verify(hymnsViewContract).hideLoading();
    }
}

Except now almost all the tests fail when I uncomment the HymnPresenter constructor.

java.lang.NullPointerException
    at com.ogotera.gusiihymnal.presenter.HymnPresenter.addHymn(HymnPresenter.java:43)
    at com.ogotera.gusiihymnal.presenter.HymnPresenterTest.addHymn_callsViewContractShowLoading(HymnPresenterTest.java:64)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)

What could be the problem?

I recommend you look into Mockito as it is the go to mocking library for Android. You are on the right track with your code structure, using the Contract pattern to have interfaces define your requirements.

You can then setup JVM tests (tests that run on your machine vs the device) where you can test your presenter.

I recommend reading through this tutorial to get a better understanding of how to test your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM