Why MVP?

Most of you have heard of this framework or have already used it. In the process of understanding and simple application, we must have several problems or pain points:

  • What’s the MVP good for, and why use it?
  • How to write the MVP structure code?
  • Why is the MVP structure good for unit testing? And why should I write test code?
  • Ok, you convinced me, but I can’t write unit tests!
  • MVP many classes, but also to write test code, write up very tired ah! I don’t want to go to all this trouble!

Here to share my experience, one by one to solve these problems.


What’s the MVP good for, and why use it?

A lot of online articles, summed up down to the main following advantages:

  • The code is decoupled and the structure is clearer
  • Better scalability
  • reusability
  • Good for unit testing

In fact, the advantages are mainly compared to the traditional MVC structure, a simple comparison:

  • MVC (Model-view-controller) In the traditional MVC structure, C acts as a master Controller, processes Model data, and controls the display of View. Most of the time the Activity class is this role, we call the interface in the Activity, the interface returns data and various setText setimages are displayed on the UI.
  • MVP (Model-view-Presenter) focuses on a Presenter. It separates a Model from a View and acts as a hub. Take the Model data and process it, then leave it to the View to figure out how to display the UI.

For example, if we deal with Model, processing business logic is processing food materials and cooking. Delivering dishes to customers for presentation is the presentation of View. So MVC is the dailies. C is a self-employed boss who cooks his own dishes and delivers them to customers on a small table. MVP is the regular dining room, P is the kitchen center, SpongeBob made krabby Patty and put it in the window, ding to inform the front desk ready to deliver food, don’t care how the food was delivered to the customer. Then the waiter octopus brother picked up the meal at the window, and then ran or jumped or stepped on the wheel shoes to the customer’s hands, cooperation to complete.

The most important feature of MVP is to separate the Model business logic from the View page.

This is where most of the benefits of MVP, such as its good scalability, decoupling, and unit testing, come from.

Pure language description we may still be difficult to understand, the following actual combat projects.


How to write the MVP structure code?

The MVP structure in the sample project is a reference to Google’s official MVP sample project. Each functional module contains the following parts:

  • The Contact protocol class is not the MVP of any module, is all View and Presenter methods are extracted into the interface put here, as a general rule, protocol, convenient unified management. The following code, for example, is the Contact protocol class for the feedback page in the sample project, which provides the View and Presenter interfaces. BaseView and BasePresenter provide some basic methods, such as showProgress, which can be added as needed.
public interface FeedBackContract { interface View extends BaseView { void addFeedbackSuccess(); } interface Presenter extends BasePresenter { void addFeedback(String content, String email); }}Copy the code

  • The Model data layer is the same as the MVC structure, nothing to say.

  • Presenter handles the business logic code, processes Model data, and distributes it to the View layer’s abstract interface. Note that this is the abstract interface that sends the processed data to the View. It is a simple transfer and is not responsible for the presentation

public class FeedBackPresenter implements FeedBackContract.Presenter { private final FeedBackContract.View view; private final HttpRequest.ApiService api; public FeedBackPresenter(FeedBackContract.View view, HttpRequest.ApiService api) { this.view = view; this.api = api; this.view.setPresenter(this); } @Override public void addFeedback(String content, String email) {// Start validation of input if (stringutils.isempty (content)) {view.showtip (" Feedback cannot be empty "); return; } if (stringutils.isempty (email)) {view.showtip (" please input your email address so that we can reply to your comments promptly "); return; } view.showProgress(); Fb = new FeedBack(); fb = new FeedBack(); fb.setContent(content); fb.setEmail(email); Observable observable = ObservableDecorator.decorate(api.addFeedBack(fb)); observable.subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { if (! view.isActive()) { return; } view.dismissProgress(); View. showTip(" Feedback submission failed "); } @Override public void onNext(BaseEntity entity) { if (! view.isActive()) { return; } view.dismissProgress(); view.addFeedbackSuccess(); }}); }}Copy the code

  • View is responsible for the UI implementation presentation. For example, when a Presenter sends an action like showProgress, it is up to the View to implement the UI. It is up to the View to control whether a progress box is displayed or a drop-down refresh circle is displayed. In Google’s example, each Activity is implemented with a Fragment as a View, and the Activity is simply a container containing a Fragment that displays various controls. I think you can also use an Activity as a View. This sample code has both methods, and you can choose them according to your needs
public class FeedBackActivity extends BaseActivity implements FeedBackContract.View { private FeedBackContract.Presenter  presenter; private EditText et_content; private EditText et_email; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_feed_back); initView(); } private void initView() { presenter = new FeedBackPresenter(this, HttpRequest.getInstance().service); InitBackTitle (" feedback ").setrighttext (" submit ").setrightonClickListener (new view.onClickListener () {@override public void onClick(View v) { submit(); }}); et_content = (EditText) findViewById(R.id.et_content); et_email = (EditText) findViewById(R.id.et_email); } private void submit() {String content = et_content.gettext ().toString().trim(); String email = et_email.getText().toString().trim(); presenter.addFeedback(content, email); } @override public void addFeedbackSuccess() {public void addFeedbackSuccess(); finish(); } @Override public void setPresenter(FeedBackContract.Presenter presenter) { this.presenter = presenter; } @Override public boolean isActive() { return isActive; } @Override public void showProgress() { showProgressDialog(); } @Override public void dismissProgress() { dismissProgressDialog(); } @Override public void showTip(String message) { showToast(message); }}Copy the code

Notice that there is an isActivite method in BaseView that determines whether the view has been destroyed. I’ll do it all in BaseActivity, adding an isActivite variable that is set to true for onStart and false for onStop. When the view is returned by the presenter, check whether the view has been destroyed and then control the display. Because the interface is asynchronous, the view may have been destroyed, so there is no need to update it.

Ok, now let’s go back to some of the MVP’s strengths and maybe get a better idea (of course, it’s best to masturbate yourself).

  1. Better scalability.

    • One day the page needs to add a function, protocol class to write the corresponding P logical method, V page method, and then in the implementation of the class to write specific code.
    • If you have any errors, use Dialog instead of Toast. If you have any errors, use Dialog instead.
    • One day when the product tells you something about feedback, and we failed to make the user feel successful, just call the View abstract method in the Error callback.


  2. Decoupled, better code structure.

    • The business logic is separate from the page UI code, not mixed together, and you don’t care about the UI when changing the logic, and vice versa.
    • When you want to understand the functionality of a module, you can see the abstract methods directly in the protocol class, without worrying about the code.
    • The code can be divided up, the core business logic is written by you in P, and the implementation of the UI is written directly by others.


  3. Reusability. For example, in the registration function of this project, both the registration step 1 and Step 2 pages have the function of sending verification code, so the same P can be used and the interface of obtaining verification code can be called in it. After obtaining the verification code successfully in Step 1, the system switches to page 2. After obtaining the verification code successfully, the system starts the digital countdown.

Why is the MVP structure good for unit testing?

As mentioned earlier, the biggest feature of the MVP structure is that P separates logic from UI. There is no Android-related code in P, such as Toast, setText, etc. This means that you can write junit tests for Presenter. Tests for Java code only, do not involve any UI!! Do not run emulator tests !!!!! The speed of the takeoff test !!!!!!!!

With all the excitement, then

Why should I write test code? Isn’t it a waste of time?

In addition to bug detection and validation logic, one of the most important functions of testing is to speed up development! You read that right, more code is written, but actual efficiency increases, especially for larger and more complex applications. If I’m not being authoritative, look at the classic book Refactoring and try it yourself, and you’ll get a feel for it.


How do I write the test code?

Let’s start with two tests in Android

  • UI testing (framework Espresso is used in this project) UI testing is actually to simulate the operation behavior on the machine, let it automatically “click a certain location”, “enter some strings” and so on. It relies on android devices, and when you test it, you can see the page on your phone or emulator screen being dotted, lost, and bounced around.

    MVC, MVP, or MVABCDEFG can all be used for UI testing, so I won’t go into it here and can refer directly to the code in the sample project. UI test part of the content is also a lot of, separate out later to expand in detail.

    The UI test code is in the androidTest folder of the project, and the Test folder is the unit test code for the Junit section.

  • The Junit unit test for Presenter (Mockito is used in this project) is close to the real world, but has the disadvantage of running applications on emulators, so it is slow and slow, and there is often a need to debug interfaces. I don’t want to jump to the page and input things and click buttons, which takes time ~ and use postman tools are also troublesome, the header has to rewrite, if there are parameters encryption will be more painful.

    So, this is where you need Junit unit tests, the biggest feature is that you don’t need to run android devices, directly run code, fast!

  • Before we get down to the basics, let’s get a feel for what a unit test looks like, as shown below

Here we tested several scenarios for feedback presenters in the sample project

  • The real interface was submitted successfully. Procedure
  • The simulated interface was submitted successfully
  • Failed to submit the simulated interface. Procedure

Three Test methods, for three Test scenarios. Break through the lower left corner of the operation can be seen, a total of 852ms, less than 1 second!! The first test method takes a little more time because it actually calls the interface data. It can also be seen in the lower right corner that all three use cases have passed the test successfully, and the interface log of real call data is also printed. Perfect ~


How to write unit test code

The writing steps follow these steps

1. Create a test class for Presenter

Right-click the Presenter class -> Go To -> Test -> Create new Test



A create test class dialog box pops up, and then check the methods you want to test (you can also create them manually). Then OK, select the Test folder to complete the test class creation.









2. The test class initialization code is as follows (mockito gradle configuration, etc. Reference project build.gradle)

// Used to test data returned by the real interface private FeedBackPresenter; Private FeedBackPresenter mockPresenter; @Mock private FeedBackContract.View view; @Mock private HttpRequest.ApiService api; @ Before public void setupMocksAndView () {/ / use the Mock tags etc. Need to init first initialization MockitoAnnotations initMocks (this); // when the view calls isActive, it returns true to indicate that the UI isActive. When (view.isActive()).thenReturn(true); // Set BoreConstants. IsUnitTest = true; Presenter = new FeedBackPresenter(view, httprequest.getInstance ().service); MockPresenter = new FeedBackPresenter(view, API); }Copy the code

An important framework is used here, Mockito.

Mockito framework introduction

  • What does the Mockito framework do? Mockito framework is used to simulate data and scenarios to facilitate our testing work.
  • Why Mockito? For example, when we test P in the MVP structure, we have a question: what happens to the View when creating a Presenter object? Passing null will null the pointer. There is a lot of interface call logic, a lot of weird failure cases how to test? This time you can use mockito ~ directly simulate a view interface object, do not care about its specific implementation; Failure cases are handled directly with the when method; There are also a number of other easy methods to test, such as Verify, which is used to determine whether an object executes a method. We’ll look at each of the examples.

Many of the examples on the web are pure mock tests, where the interface API is also mock, mock interface calls, mock interface returns data. Although this is fast and easy to simulate various error conditions, there are times when you want to test real interface returns, so this project example provides two ways to write and handle simulated and real interfaces. Refer to the Presenter and mockPresenter objects in the code above.

Note that the users of mock related methods such as verify and when must also be mock objects, so you cannot use the when method to simulate the return of an interface.

The @before tag is a method that goes through every test method call, so it puts a series of initialization operations in it, each annotated. One that needs to be explained separately is the when method.

when(view.isActive()).thenReturn(true);Copy the code

Mockito is this framework provides a method of reading English basic can understand what do you mean, when xx method invocation returns xx because our view of simulation, so there is no implementation isActive method, the p in the data after return will not be able to continue to go down, so here the when dealing with it. Returns true whenever this method is called.

Usually one business method in Presenter will correspond to at least one test method. For example, the feedback business here corresponds to two scenarios of successful and failed submission of opinions. The method name can be determined by the @test tag. The recommended method name is: Test + method to be tested original name + Test scenario What are the Test scenarios? This is best done by asking the test for a test case that corresponds to all the scenarios of the function under test.

The unit test code I wrote here divides interfaces into two types: ** analog interface ** and ** real interface **

Why mock tests? Well, like our feedback, unlike logins and incorrect passwords, there are very few scenarios where you can fail. How to do? So for difficult to simulate the scene, or need to use mockito framework simulation, simulation of a failure, and then verify the failure of a series of logic ~

The following test methods are introduced one by one, and simulation successes and failures are pretty much just failures.

  • Sample simulated interface test method – Simulated commit failed
@test public void testAddFeedback_Mock_Error() throws Exception { Error when(api.addfeedback (any(feedback.class))). ThenReturn (Observable. Error (new Exception). ))); String Content = "This App is hot!" ; String email = "[email protected]"; mockPresenter.addFeedback(content, email); verify(view).showProgress(); verify(view).dismissProgress(); Verify (view).showtip (" feedback submission failed "); }Copy the code

The focus here is on the use of when, which returns an error when the simulated API calls addFeedBack. MockPresenter’s opinion feedback business method is then called and the results are verified. Note that this verify method is also a particularly common Mockito method used to verify that an object executes a method. Finally run the test, success, perfect ~

  • Example of real interface test method – Submitted successfully

@test public void testAddFeedback_Success() throws Exception {// Real data. String Content = "This App is really good!" ; String email = "[email protected]"; presenter.addFeedback(content, email); verify(view).showProgress(); verify(view).dismissProgress(); verify(view).addFeedbackSuccess(); }Copy the code

Here we use the Presenter object corresponding to the real interface, call the interface, and verify the success result. Run the test, success, perfect ~

Again, mockito’s methods are for mock objects, so if you call the real request API and try to use when, you will get an error ~

Note that the real interface is asynchronous, so it cannot pass the test without doing any processing. The following validation fails before the interface data is returned. So you need to do something about the callback, change it to a synchronous request, so that you can run a line down and validate it after the interface is run. The project is based on the Retrofit framework and uses RxJava to handle callbacks. All of my callbacks here are handled by an ObservableDecorator, in which I determine that if the current state is a test (that is, the isUnitTest parameter in Before), Set the callback to sync, refer to the code project.

4. Run the unit test case

  • Right-click the method, run tests a single use case method
  • Right-click the class, and run tests all of the use case methods contained in the class

Finally, the console looks at the results. Refer to the screenshot in the unit test code sample at the top. The console below shows which methods were tested, how many methods were successfully passed, and then prints the corresponding log.

Ok, the writing method is introduced ~ more examples please go to the project to view, here is limited space is not too detailed to expand, simply list a few examples to let you feel.


MVP many classes, but also to write test code, write up very tired ah! I don’t want to go to all this trouble!

This is probably the most important thing that keeps most people out. After all, ordinary ordinary masturbation is so tired, but also so troublesome, no time no energy ah!!

  • You don’t have to use MVP for everything like in the previous example, food stalls and regular restaurants. You’re in the middle of nowhere with no traffic and the whole kitchen center. In the same way, if you have a feature with simple business logic, there’s no need for an MVP, and you might be overcharging for a simple page, so you don’t have to use the MVP for all features.

  • Unit testing is good for developing code structure, but it can be really handy sometimes, especially when it runs fast. I believe that most people have experience, encounter a unreliable background, often have to accompany them to adjust the interface, and then encounter that special deep page is a waste of life. Unit test code, run, whisk ~ seconds done. Self-testing certain logic functions is also useful, which seems to be a definite time saver.

  • LiveTemplate (dry stuff!! One key to generate template code, template can be customized!! I’m usually really, really focused on speed when I masturbate. A lot of plug-in work has been done before, such as the released open source AndroidStudio plug-in that automatically generates code layout. Github.com/boredream/B…

    Then I thought, when writing the MVP classes and test classes that are very regular, why not create a plugin? But what about the template code generated by the plugin? MVP is written differently for different people. Finally, I remembered LiveTemplate in AndroidStudio, which is a template code system in AS.

Use the LiveTemplate template

Just to show you how powerful this is, I’ve written a few templates in advance. Take the protocol class for example.

1. Right-click the location you want to generate -> New -> select template (MvpContract as shown below)



2. In a dialog box that is displayed, enter required variables for the template and OK is generated



3. In this way, a file is created according to our template. The code of the file on the right side is automatically generated.













So where does the template come from

Edit/create the LiveTemplate template

  1. To Edit an existing Template: New -> Select an Edit File Template at the bottom of the Template, click on it. See the figure above using Step 1.
  2. Create a new Template: Open the File from which you want to generate the Template and choose Tool -> Save File as Template from the toolbar
  3. Step 1 and step 2 will open the following edit page, the difference is to create less than edit the left side of the existing template list, give the template a NAME, and then in the content page to delete and modify as needed, all the template will be replaced by the NAME of the file you entered when creating the template. ${XXX} ${XXX} ${XXX} Finally, OK saves the template.

LiveTemplate can’t handle most of the code for you, but such a quick template, flexible editing is very convenient, or can save a considerable amount of code.

As an aside from this topic, LiveTemplate is an amazing thing that can be used in a lot of different places, not only for template files, but also for code. For example, typing sout+ return will automatically generate system.out.print () code, typing Toast+ return will automatically generate toast.make blablabl code, super convenient. For example, if you have a BaseActivity project and need to duplicate several methods, you can create a custom page class file template to handle inheritance and methods, so you don’t have to write inheritance every time you create a new Activity. Look forward to more usage


conclusion

Well, all the questions and pain points have been answered one by one, especially the last LiveTemplate. For those who don’t know, even if you still don’t want to use MVP and write unit tests in the end, you still have earned this part.

Because the content to introduce more, MVP ~ test ~Junit unit test ~LiveTemplate ~ so the introduction is relatively simple, the purpose is to throw a brick to attract jade, I hope you can have an understanding of these things, after interest in further research, also hope to communicate with me more we make progress together.

The Junit test module in this project actually has a few problems. For example, I use the Api as a dependency injection constructor parameter, so it can be improved by adding Dagger2. The next framework will add Dagger2 to the MVP structure.

Retrofit2+RxJava is a generic framework for all examples, so I’m not going to rule out Retrofit.

Finally, if the article is useful and enlightening to you, I hope you can support it. Welcome to follow me and star in this project