Unit Testing in Android

Source: Internet
Author: User
1. Testing for contentprovider

Before writing a case for the provider, you should carefully read the instructions on provider testing in the SDK documentation. However, you still cannot write the correct case when reading the instructions, because you also know that the android documentation is poor and there are some key things that are not described in the documentation. You also know that, this is not uncommon in Android.
You can write a provider case as follows:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {}

There is a compilation error. It indicates that providertestcase2 does not have an implicit structure. It seems that we need a constructor to write a standard JUnit constructor!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {    public FeedProviderTest(String name) {        super(name);    }}

WTF, there are still compilation errors, and more serious! Does providertestcase2 not inherit from testcase? With the eclipse suggestion, it creates a structure with two parameters:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {    public FeedProviderTest(String name) {        super(name);    }        public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);        // TODO Auto-generated constructor stub    }}

However, the feedprovidertest (string name) with only one name is still incorrect. Try again without parameters. This means that providertestcase2 does not have such a constructor, but it makes no sense, because it is inherited from testcase after all! It's amazing and weird!
Since providertestcase2 does not have a parameter construction, we can only remove the construction with a parameter!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {    public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);    }        public void testConstructor() throws Throwable {        assertNotNull("can construct resolver", getMockContentResolver());        ContentProvider provider = getProvider();        assertNotNull("can instantiate provider", provider);    }}

After a basic test is written and run, a warning is obtained. It is reported by JUnit framework that demoprovidertest does not define the common constructor testcase (name) or testcase (), what is the situation? I don't define it, but there is a compilation error, because the damn providertestcase2 doesn't have these two structures! Damn, you can only add this structure back! However, because the parent class does not exist, the structure of the dual parameters of the parent class can only be referenced!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {      public DemoProviderTest() {        super(null, null);    }        public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);    }        public void testConstructor() throws Throwable {        assertNotNull("can construct resolver", getMockContentResolver());        ContentProvider provider = getProvider();        assertNotNull("can instantiate provider", provider);    }}

But what does the Parameter Pass? Try null first! There is a complete error. NPE occurs during the initialization of the parent class. This indicates that null is definitely incorrect! After reading the imposed demoprovidertest (class <feedprovider> providerclass, string providerauthority) constructed with two parameters, we also say that we should pass a class object and the authority of the provider, and try again!

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {    public DemoProviderTest() {        super(FeedProvider.class, AUTHORITY);    }        public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);    }        public void testConstructor() throws Throwable {        assertNotNull("can construct resolver", getMockContentResolver());        ContentProvider provider = getProvider();        assertNotNull("can instantiate provider", provider);    }}

This okay is finished, but the construction of the two parameters is meaningless, so that one parameter calls the two parameters:

    public DemoProviderTest() {        this(FeedProvider.class, AUTHORITY);    }

Or okay, which indicates that our case must provide correct construction parameters for providertestcase2!
Add setup and teardown:

    @Override    public void setUp() throws Exception {        mContentResolver = getMockContentResolver();    }        @Override    public void tearDown() throws Exception {        mContentResolver = null;    }

Run and find that testconstructor is down. It means that the returned value of getmockcontentresolver () is null. How is this possible? It's so strange! It is still possible that the initialization is incorrect. A parent class call is added to setup:

    @Override    public void setUp() throws Exception {        super.setUp();        mContentResolver = getMockContentResolver();    }        @Override    public void tearDown() throws Exception {        super.tearDown();        mContentResolver = null;    }

Now, all the operations are okay. It means that all methods that involve override parent classes must call the methods of parent classes to initialize them correctly! The following is the correct full version:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {    private ContentResolver mContentResolver;        public DemoProviderTest() {        this(FeedProvider.class, AUTHORITY);    }        public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);    }        @Override    public void setUp() throws Exception {        super.setUp();        mContentResolver = getMockContentResolver();    }        @Override    public void tearDown() throws Exception {        super.tearDown();        mContentResolver = null;    }        public void testConstructor() throws Throwable {        assertNotNull("can construct resolver", getMockContentResolver());        ContentProvider provider = getProvider();        assertNotNull("can instantiate provider", provider);    }}

To sum up, the experience gained from this example is that all component tests must be inherited from Android. test. * The following component test framework must pass correct parameters to these component test frameworks; otherwise, the case cannot be tested:
Two Constructors

    public DemoProviderTest() {        this(FeedProvider.class, AUTHORITY);    }        public DemoProviderTest(Class<FeedProvider> providerClass,            String providerAuthority) {        super(providerClass, providerAuthority);    }

A single Constructor (with a string or no parameters) must call the specified constructor of the test architecture to pass the correct parameters to the test framework!
In addition, when rewriting the parent class method, you must also call the parent class method; otherwise, the initialization will not be correct!
But here I have to say that these component testing frameworks are really difficult to write. First of all, that name is confusing. Why is there a 2! Android is really enough for 2! Also, as a framework, initialization should be complete and complete, so that the framework can be called. Users should only need to inherit and finish their work. Just like the component activity or contentprovider, when you get to your code, the initialization work in the framework has been completed. Therefore, the successor only needs to care about your own initialization work! However, the testing framework is poor. The successor should not only care about its own initialization, but also ensure that the correct parameters are passed to the parent class!
2. Testing for activity

Similarly, you should pay attention to initialization for activity testing, but it does not matter if you do not call Super for setup and teardown!

public class ExplorerActivityTester extends        ActivityInstrumentationTestCase2<ExplorerActivity> {    public ExplorerActivityTester() {        this(TARGET_PACKAGE_NAME, ExplorerActivity.class);    }        public ExplorerActivityTester(String pkg, Class<ExplorerActivity> class1) {        super(pkg, class1);    }        @Override    public void setUp() {        mInstrumentation = getInstrumentation();    }}
3. Obstacles to Unit Testing

In Android, it is especially difficult to write unit test cases and verification test cases for Android due to the features of its system architecture.

A. Activity Reuse

The reason is that each test package and the test package is also an APK. Each package can only inject one target APK, that is, only the content in one APK can be tested, once an operation jumps beyond the APK, it is beyond the control of the test framework. However, the Component reuse mechanism is very common in Android. It uses intent to jump to other applications (APK) and call other application components to complete an operation. This is a feature of Android, that's even more common! However, this poses insurmountable obstacles to unit test cases. The testing framework itself is weaker. Once a component exists, instrumentation cannot control it. The open-source testing framework robuw.solo solves this problem to some extent, solo can operate any component in a package, especially it can solve the problem of multiple activity jumps, but as mentioned above, because a test APK can only inject one target APK, so once the activity jumps out of the application, Solo has no way. This is an unsolved problem. Therefore, for Android testing, you can only focus on some code that is far away from surface operations, such as the logic layer, API layer, Data, provider, and service! When the surface activity jumps, only some tests can be performed or mockobject can be used to solve the problem. However, this usually loses the meaning of the test because it takes a lot of time to create a mockobject, which is not worth it!
B. actionbar is not clickable

Another disgusting problem is that the actionbar of the activity cannot be directly clicked. I really don't know what Google is doing, and a new stuff is created, so the test framework does not support operations! You can only click screen coordinates through solo on the actionbar, which is very difficult to transplant and maintain!
Speaking of operations, we have to say that the native framework instrumentation supports very few operations and is not easy to use. It can only distribute keyevent events, which are not useful in many cases, such as a dialog box, if you want to click okay or cancel, It is very troublesome. If you want to click an item in a listview, it is also very troublesome! Similarly, the third-party robotium-solo framework is much easier to use. It is well encapsulated. With solo. clickontext (), you can easily click the view with this text on the screen. Its internal implementation is to use the view display tree to search for the related view based on the tag (text), and then send the click event to it! This also explains why solo cannot click the actionbar, because the actionbar is not in the view of the activity. It is a system-level thing like statusbar!
C. statusbar belongs to settings.apk

It is hard to imagine that the ubiquitous statusbar actually belongs to the settings, and the statusbar can be operated only when the settings package is injected. Testing package of supervisor!

4. Security Concern

The tested code (instrumentation and testrunner) also exists in the form of an APK, which can be injected into any target APK, and then can be operated on, or even obtain its resources and data. This brings security issues! You can use an APK with test code as an application. Once running on a mobile phone, you can operate on any application.
In fact, this is not a problem if the app market can strictly test and review apps uploaded by developers. But now the problem is that neither Google Play nor other markets are doing very well, so it will give bad people a chance!
In fact, the key issue here is that android vendors should not blindly pursue the quantity! Apple has come up with the idea of centralized application sales, and Apple's App Store is also the best! Android is just a replicaset, so your development is slow, the number is small, the quality is not enough, the income is not good, it is normal, because you are a follower, you started late! For the vendor, you cannot control the quantity and cannot create tens of thousands of applications at once. This takes time. However, at least, you can strictly control the quality! You can perform a strict test on the uploaded application. This is the responsibility of the user and the responsibility of the user! Therefore, both devices and applications require better quality for Apple, and Android is always defective. Therefore, the price for Apple products is high and Android products is cheap, of course, the price is also the only advantage of Android! In today's society, money is collected and goods are sold. Cheap, naturally, there is no good goods!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.