17

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.

James
  • 3,485
  • 3
  • 20
  • 43
  • I don't have a proper answer, but I recently went around singletons and static classes by replacing them with a service locator pattern. It made them easily testable and took a few minutes for each class to convert. – manabreak Aug 18 '15 at 13:56
  • 3
    I don't know so much about testing in other languages, but in Java, `static` is the enemy of testing. – Solomon Slow Aug 18 '15 at 14:28
  • You might want to have a look at PowerMock (https://github.com/jayway/powermock). It's a test framework that allows you to test difficult cases. – Micho Aug 19 '15 at 14:12
  • PowerMockito just gives PowerMock Mockito-flavored API, technically they both do the same – iirekm Nov 12 '15 at 17:58

4 Answers4

9

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")

Community
  • 1
  • 1
iirekm
  • 8,890
  • 5
  • 36
  • 46
4

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.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
0

You can use mockito latest version i.e 3.4.+ which allows static mocking

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#48

Qumber Abbas
  • 560
  • 1
  • 4
  • 11
0

Here are some solutions:

  • Modify your static method as a non-static method or wrap your static method with a non-static method, and then directly use Mockito to mock the non-static method in the Android Test.
  • If you don't want to change any original code, you can try to use a dexmaker-mockito-inline-extended to mock static methods and final methods in the Android Test. I successfully mocked the static methods with it. Check this solution.
  • Use a Robolectric in the Unit test and then use PowerMock to mock the static methods in the Unit Test.
Dharman
  • 30,962
  • 25
  • 85
  • 135