Preface
If you're looking for a better project structure on the Web, you'll see hundreds of blogs explaining the pros and cons of various architectures. Unfortunately, most articles do not mention a very important point: unit Testing
When we choose a project structure, the decisive factor is nothing more than a person's preference or project needs. I don't think the MVP architecture is better than the MVVM architecture, or the MVP architecture is a perfect customer solution. The only reason I decided to use the MVP architecture was the simplicity of it.
MVP The MVP representative Model-view-presenter Model is usually understood as data source, whether from a network or a database, or even a handwritten list object can be considered a data source view can generally use activity , fragment, or a custom view to act as. The main function of the view layer is to display the interface, blocking user interaction events presenter in which they play the role of POJO (plain old Java object). It is responsible for communication between the View layer and the Model layer.
Attention:
When we go to achieve a certain presenter, we must pay attention to the Model layer may appear error information to presenter to do the unified treatment. And try to pull the business logic out of the UI layer and put it into the presenter. The first principle of implementing the MVP Architecture Test Principle is to use junit rather than espresso or other third-party automated test frameworks, followed by separate tests for each layer, This is diametrically opposed to the integration test. Therefore, some dependency injection frameworks need to be understood. I recommend the current Hot dagger framework because we are using junit, so we need a separate framework to test the UI functionality. I recommend this is the current more mature robolectric framework. The use of the Mockito framework is also necessary because it is basically the most popular mock test framework in the Test model layer
Model cannot hold presenter and View layer reference
model in the presenter layer should be as far as possible in the form of a simple interface, especially when we use the three-party framework of the
model layer test should never be dependent on the project architecture , it should be independent
Case:
The model interface is created to provide the user with relevant data (we do not know whether the data is from a network or a database) because Rxjava is used so the return type is observable
Public interface Profileinteractor {
observable<userprofile> getprofile ();
}
and testing this interface method can be directly used Rxjava provided to our Testsubscriber class, as shown below:
public class Profileinteractortest {
private static final String USER = "USERNAME";
Profileinteractor interactor;
@Before public
void SetUp () {
interactor = new Profileinteractorimpl (...);
}
@Test public
void Testgetuserprofile () throws Exception {
testsubscriber<userprofile> subscriber = Testsubscriber.create ();
Interactor.getprofile (). Subscribe (subscriber);
Subscriber.assertnoerrors ();
Subscriber.assertcompleted ();
Assertthat (Subscriber.getonnextevents (). Get (0). GetName ()). Isequalto (USER);
}
test the View layer
The test of the View layer is relatively simple, and the difficulty lies in the configuration of the robolectric. First, let's look at the view interface.
Public interface Profileview {
void display (UserProfile userprofile);
}
The case we chose was to use an Android custom view to act as the view layer
public class Profileframelayout extends Framelayout implements Profileview {private Profilepresenter presenter;
@BindView (r.id.text_username) TextView textusername;
@Inject public void Setpresenter (Profilepresenter presenter) {this.presenter = presenter;
Presenter.attachview (this);
Public Collectionframelayout {Super (context);
Init ();
Public Collectionframelayout (context, AttributeSet attrs) {Super (context, attrs);
Init ();
private void init () {View view = Inflate (GetContext (), r.layout.view_profile, this);
Butterknife.bind (view);
} @Override protected void Onattachedtowindow () {Super.onattachedtowindow ();
Presenter.attachview (this);
} @Override protected void Ondetachedfromwindow () {Super.ondetachedfromwindow ();
Presenter.detachview (); @Override public void Display(UserProfile userprofile)
{Textusername.settext (Userprofile.getname ()); }
}
So here's the question: what part of the View should we test, or which code should write the test code?
The answer is: EVERYTHING. All of them must be tested in place.
1 Test whether the view was successfully created
2 test default value is correct
3 test user interaction is passed to presenter
4 test View Just do what it should (show interface)
@RunWith (Robolectricgradletestrunner.class) @Config (constants = buildconfig.class) public class
profileframelayouttest {private Profileframelayout profileview;
@Mock Profilepresenter presenter;
@Before public void SetUp () throws Exception {mockitoannotations.initmocks (this);
Profileview = new Profileframelayout (runtimeenvironment.application);
Profileview.setpresenter (presenter);
@Test public void Testempty () throws Exception {Verify (presenter). Attachview (Profileview);
Asserthat (ProfileView.textUsername.getText (). toString ()). IsEmpty ();
@Test public void Testleaveview () throws Exception {Profileview.ondetachedfromwindow ();
Verify (presenter). Detachview ();
@Test public void Testreturntoview () throws Exception {reset (presenter);
Profileview.onattachedtowindow ();
Verify (presenter). Attachview (Profileview); @Test public void TestDisplay () throws Exception {userprofile user = new UserProfile (user);
Profileview.display (user);
Asserthat (ProfileView.textUsername.getText (). toString ()). Isequalto (USER); }
}
Explanation:The top note is to declare a profileframelayout global reference to the Robolectrie configuration, mainly to test it using Mockito to create a mock presenter, because we just want to verify the View through it Whether the layer will successfully invoke the relevant code to the presenter layer because the creation of view in the Android code must be passed into a context, so we use robolectric to provide our runtimeenviroment as context, and passes it to the Profileframelayout object, passing the presenter object to it
Test presenter Layer
The way to test presenter and test model is almost just this time we're mocking the view layer. Presenter will receive a view object and a model layer interface object to display the interface and get the data
public void Profilepresenter {
private final profileinteractor interactor;
Private Profileview view;
Public Profilepresenter (Profileinteractor interactor) {
this.interactor = interactor.
}
public void Attachview (Profileview view) {
This.view = view;
Fetchanddisplay ();
}
public void Dettachview () {
//not covered by this example:
//You should handle the subscription
}
PU Blic void Fetchanddisplay () {
//not covered by this example:
//Your should handle the subscription
//You should also check if not NULL
//You should also handle the OnError
interactor.getuserprofile (). Subscrib E (userprofile-> view.display (userprofile));
}
For the presenter layer test, our main goal is to ensure the success of the data from the Model layer and successfully passed to the View layer display, we do not care about the correct data.
But if you have a transformation of the data at the presenter level, then you need to verify the correctness of the data.
The code is as follows:
public void Profilepresentertest {
@Mock
profileinteractor interactor;
@Mock
Profileview view;
@Before public
void SetUp () throws Exception {
mockitoannotations.initmocks (this);
When (Interactor.getuserprofile ()). Thenreturn (Observable.just (New UserProfile ()));
Presenter = new Profilepresenter (interactor);
Presenter.attachview (view);
}
@Test public
void testdisplaycalled () {
Verify (interactor). GetUserProfile ();
Verify (View). Display (any ());
}
SummaryCreate a mockable Model, and if not, then abstract and then encapsulate the use of a Dependency injection framework (dagger) to automatically inject presenter objects into the view layer without manually creating presenter objects in view instead of focusing only on the test output data, Also focus on the interaction between objects if presenter is dependent on the declaration cycle of the View, then we must test it. Test visual changes from your View, not only the text, but also ity or Background color if you change it. Test the view layer to the extreme, not just the text test, but also the background color, visibility and so on to test Test presenter different responses to the model layer when providing different data
Reference Link: https://medium.com/@Miqubel/testing-android-mvp-aa0de6e165e4