In the last article, I decided to use Powermock to write unit tests based on research and analysis.

There are plenty of articles on the web explaining how Powermock can be used, but here are just a few of the common postures on Android.

This article will be improved over time.

Mock vs Spy

Powermock provides both mock and Spy, and method emulation is often required to validate the invocation of an Activity’s private methods. Mock can be implemented as well as Spy, and mock is the default for all methods. Spy is not emulated for all methods by default.

My personal recommendation is to mock, because there is a lot of method logic in an activity, and for a unit test, we tend to only test one method and mock all the others. To validate the return value of a call or mock method, etc.

findViewById

FindViewById () is a Stub that will return an error. The usual practice is to mock an activity, but the findViewById() of the mock method returns null.

Example: Verify that the activity onCreate has a click-listen set for the View.

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_launcher);
        mSkipView = findViewById(R.id.skip);
        mSkipView.setOnClickListener(this);
    }
Copy the code

Test code:

@PrepareForTest({LauncherActivity.class, Build.VERSION.class})
public class LauncherActivityTest extends PowerMockTest {

    @Mock
    LauncherActivity activity;

    @Mock
    View mSkipView;

    @Test
    public void onCreateSdk19(a) throws Exception {
        PowerMockito.doCallRealMethod().when(activity, "onCreate", ArgumentMatchers.any());
        // Return the mock View object when findViewById(r.id.skip) is called
        PowerMockito.doReturn(mSkipView).when(activity).findViewById(R.id.skip);

        activity.onCreate(null);
        
        // Whether to set the listener
        Mockito.verify(mSkipView).setOnClickListener(ArgumentMatchers.any());
    }

Copy the code

Whitebox.setInternalState()

Final and private field assignments.

 Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Integer.valueOf(18));
Copy the code

In the same way, there are methods of obtaining…

You need to add @prepareFortest

PowerMockito.supress()

The Activity declaration cycle method usually has a super.oncreate () method, but the super method does not execute, and will report an error Stub once executed.

PowerMockito.suppress(Whitebox.getMethod(AppCompatActivity.class, "onCreate", Bundle.class));
Copy the code

In this way, the parent AppCompatActivity’s onCreate() will not be executed when testing activity.oncreate ().

PowerMockito.whenNew()

How do you verify a page jump?

It is now commonly used to verify the intent to determine whether the intent corresponding to the package name is constructed.

Declares the Intent object to mock when new Intent() is returned.

 Intent intent = PowerMockito.mock(Intent.class);
 PowerMockito.whenNew(Intent.class).withNoArguments().thenReturn(intent);
 PowerMockito.whenNew(Intent.class)
                .withParameterTypes(Context.class, Class.class)
                .withArguments(ArgumentMatchers.any(Context.class), ArgumentMatchers.any(Class.class))
                .thenReturn(intent);
Copy the code

validation

 PowerMockito.verifyNew(Intent.class).withArguments(activity, MainActivity.class);
 Mockito.verify(intent).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Copy the code

Whether the intent for MainActivity is constructed and whether flags are added.

PowerMockito.thenAnswer()

When constructing a dialog, there is usually code like this:

private void showExpiredDialog(a) {
        if (mAlertDialog == null| |! mAlertDialog.isShowing()) { mAlertDialog =new AlertDialog.Builder(this)
                    .setMessage("Your login status has expired. Please log in again.")
                    .setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // 
                            startActivity(AppHelper.makeMainIntent(LogoutDialogActivity.this));
                        }
                    })
                    .create();
            mAlertDialog.setCancelable(false); mAlertDialog.show(); }}Copy the code

How to verify that the contents of setPositiveButton’s OnClickListener callback logic are correct.

  @Test
    public void logout(a) throws Exception {
        PowerMockito.doCallRealMethod().when(activity, "showExpiredDialog");
        // Test the callback logic
        PowerMockito
                .when(builder.setPositiveButton(ArgumentMatchers.anyInt(), ArgumentMatchers.any(DialogInterface.OnClickListener.class)))
                .thenAnswer(new Answer() {
                    @Override
                    public Object answer(InvocationOnMock invocation) throws Throwable {
                        Object[] args = invocation.getArguments();
                        // Get the second argument object
                        DialogInterface.OnClickListener arg = (DialogInterface.OnClickListener) args[1];
                        // Execute the callback directly
                        arg.onClick(null.0);
                        returninvocation.getMock(); }}); Whitebox.invokeMethod(activity,"showExpiredDialog");
        
        PowerMockito.verifyStatic(AppHelper.class);
        AppHelper.makeMainIntent(ArgumentMatchers.any(Context.class));
    }
Copy the code

When setPositiveButton() is executed, the callback in Answer is executed, the callback method is run directly, and then the corresponding method is verified.

Whitebox.invokeMethod()

Usually some private methods need to be run, and writing your own reflection needs to be modified.

Whitebox.invokeMethod(activity, "showExpiredDialog");
Copy the code