How to android unit test and mock a static method

2019-02-06 08:11发布

Hi I really hope you can help me, I feel like I've been pulling my hair out for days.

I'm trying to write unit tests for a method A. Method A calls a static method B. I want to mock static method B.

I know this has been asked before, but I feel Android has matured since then, and there must be a way to do such a simple task without re-writing the methods I want to test.

Here is an example, first the method I want to test:

public String getUserName(Context context, HelperUtils helper) {
    if(helper == null){
        helper = new HelperUtils();
    }
    int currentUserId = helper.fetchUsernameFromInternet(context);

    if (currentUserId == 1) {
        return "Bob";
    } else {
        return "Unknown";
    }
}

Next the static method I want to mock:

public class HelperUtils {
    public static int fetchUsernameFromInternet(Context context) {
        int userid = 0;

        Log.i("HelperUtils ", "hello");

        return userid;
    }
}

In other languages this is so easy but I just can't make it work in Android. I've tried Mockito, but it appears static methods aren't supported

HelperUtils helper = Mockito.mock(HelperUtils.class);
Mockito.when(helper.fetchUsernameFromInternet(getContext())).thenReturn(1);

This errors

org.mockito.exceptions.misusing.MissingMethodInvocationException

I've tried Powermock but I'm not completely sure this is supported by Android. I managed to get powermock running using androidCompile in my gradle file but I get this error:

Error:Execution failed for task ':app:dexDebugAndroidTest'. com.android.ide.common.process.ProcessException:

Not to mention PowerMockito.mockStatic(HelperUtils.class); Doesn't return anything, so I don't know what to pass into my getUsername method!

Any help would be so very much appreciated.

2条回答
老娘就宠你
2楼-- · 2019-02-06 08:14

Static methods aren't related to any object - your helper.fetchUsernameFromInternet(...) is the same (but a bit confusing) as HelperUtils.fetchUsernameFromInternet(...) - you should even get a compiler warning due to this helper.fetchUsernameFromInternet.

What's more, instead of Mockito.mock to mock static methods you have to use: @RunWith(...), @PrepareForTest(...) and then PowerMockito.mockStatic(...) - complete example is here: PowerMockito mock single static method and return object

In other words - mocking static methods (and also constructors) is a bit tricky. Better solution is:

  • if you can change HelperUtils, make that method non-static and now you can mock HelperUtils with the usual Mockito.mock

  • if you can't change HelperUtils, create a wrapper class which delegates to the original HelperUtils, but doesn't have static methods, and then also use usual Mockito.mock (this idea is sometimes called "don't mock types you don't own")

查看更多
闹够了就滚
3楼-- · 2019-02-06 08:21

I did this way using PowerMockito.

I am using AppUtils.class, it contains multiple static methods and functions.

Static function:

public static boolean isValidEmail(CharSequence target) {
    return target != null && EMAIL_PATTERN.matcher(target).matches();
}

Test case:

@RunWith(PowerMockRunner.class)
@PrepareForTest({AppUtils.class})
public class AppUtilsTest {

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        PowerMockito.mockStatic(AppUtils.class);

        PowerMockito.when(AppUtils.isValidEmail(anyString())).thenCallRealMethod();
    }

    @Test
    public void testValidEmail() {
        assertTrue(AppUtils.isValidEmail("name@email.com"));
    }

    @Test
    public void testInvalidEmail1() {
        assertFalse(AppUtils.isValidEmail("name@email..com"));
    }

    @Test
    public void testInvalidEmail2() {
        assertFalse(AppUtils.isValidEmail("@email.com"));
    }
}

Edit 1:

Add following imports:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

Hope this would help you.

查看更多
登录 后发表回答