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.