preface

We shared the refactoring process for removing dependencies. There are four main steps: identify the cohesive package, remove the dependency, move, and accept. At the same time, finally, I also raised a question, how to ensure the correctness of the function when refactoring, not to modify the new problem?

The problem is easy but not easy. Easy is to modify the function of a careful test to ensure that all functions can be normal. What’s not easy is how to perform the process comprehensively, efficiently, and repeatably. An easy solution to think of is automated testing. But the biggest problem is that for most legacy systems there are no automated tests. And with a lot of bad-smelling code and low testability, it’s hard to adequately automate testing. So do we have a compromise strategy?

The test strategy

Let’s start by looking at the Google Android Developer website’s introduction to testing, which divides the different types of tests into three categories (small, medium, and large).

Image from developer.android.com

  • Small tests are unit tests that verify the behavior of an application, one class at a time.
  • Intermediate tests are integration tests that verify interactions between stack levels within a module or between related modules.
  • Large scale tests are end-to-end tests that validate user processes across multiple modules of the application.

As mentioned earlier, legacy monomer systems typically do not have any automated testing and are often heavily coupled internally, so the cost of implementing small to medium size systems is very high. Obviously, for legacy systems, the test pyramid model is less applicable. Therefore, for legacy systems, the appropriate strategy model may be as follows:

For the legacy unit system, a feasible idea is to supplement the medium and large scale test as the basic smoke test, and then supplement the small and medium scale test in time after refactoring and optimizing the internal structure.

CloudDisk sample

For our condensed version of CloudDisk, the interface is also relatively simple. There is a main interface, the main interface for files, dynamic, user. (Future MV* refactoring will continue to add page interaction and logic)

We can design a set of UI tests to verify the basic functionality. The main test points are as follows:

  1. The main screen runs properly and displays three fragments
  2. The three fragments can be displayed properly
  3. Click the login button to jump to the login page

Test design uses the following example:

@RunWith(AndroidJUnit4.class) @LargeTest public class SmokeTesting { @Test public void should_show_fragment_list_when_activity_launch() { //given ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class); scenario.onActivity(activity -> { //when onView(withText(R.string.tab_user)).perform(click()); //then List<Fragment> fragments = activity.getSupportFragmentManager().getFragments(); assertThat(fragments.size() == 3); assertThat(fragments.get(0) instanceof FileFragment); assertThat(fragments.get(1) instanceof DynamicFragment); assertThat(fragments.get(2) instanceof UserCenterFragment); }); } @Test public void show_show_file_ui_when_click_tab_file() { //given ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class); scenario.onActivity(activity -> { //when onView(withText(R.string.tab_file)).perform(click()); //then onView(withText("Hello file fragment")).check(matches(isDisplayed())); }); } @Test public void show_show_dynamic_ui_when_click_tab_dynamic() { //given ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class); scenario.onActivity(activity -> { //when onView(withText(R.string.tab_dynamic)).perform(click()); //then onView(withText("Hello dynamic fragment")).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); }); } @Test public void show_show_user_center_ui_when_click_tab_dynamic() { //given ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class); scenario.onActivity(activity -> { //when onView(withText(R.string.tab_user)).perform(click()); //then onView(withText("Hello user center fragment")).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); }); } @Test public void show_show_login_ui_when_click_login_button() { //given ActivityScenario<MainActivity> scenario = ActivityScenario.launch(MainActivity.class); scenario.onActivity(activity -> { Intents.init(); //when onView(withId(R.id.fab)).perform(click()); //then intended(IntentMatchers.hasComponent("com.cloud.disk.platform.login.LoginActivity")); Intents.release(); }); }}Copy the code

See Github commit for details

We can run the use case on Robolectric to speed up the feedback by executing the following command:

./gradlew testDebug --tests SmokeTesting
Copy the code

The test results are as follows:

Of course, in a real project the situation is more complex, the data may come from network services, databases, etc. We also need to Mock. Subsequent MV* refactorings will continue to be supplemented with common bad taste sample code and more automated test cases.

For more testing framework and design, please refer to Google’s official test application on Android platform

conclusion

In this paper, we introduce the commonly used test classification and the test strategy of legacy system. For legacy unit system, a feasible idea is to supplement medium and large scale tests first, as the basic smoke test, and then supplement medium and small scale tests in time after refactoring and optimizing the internal structure. It also adds a base set of large tests to CloudDisk as smoke tests, which serve as a basic daemon test for subsequent refactoring.

In the next part of Mobile Application Legacy System Refactoring (7) – Decoupled Refactoring Demonstration (1), we will start to reconstruct CloudDisk based on the process in the method section, and the specific decoupled operation will be shown in the way of video.

The resources

developer.android.com

CloudDisk example code

CloudDisk

Series of links

Refactoring legacy Systems for mobile Applications (1) – Start

Refactoring legacy systems for mobile applications (2) – Architecture

Refactoring legacy systems for mobile applications (3) – Examples

Refactoring legacy Systems in Mobile Applications (4) – Analysis

Mobile application legacy System refactoring (5) – Refactoring methods

The outline

about

Welcome to the CAC Agile Coach public account. Wechat search: CAC Agile Coach.

  • Author: Huang Junbin
  • Blog: junbin. Tech
  • GitHub: junbin1011
  • Zhihu: @ JunBin