I have this production method:
public boolean onShouldOverrideUrlLoading(String url) { boolean isConsumed = false; if (url.contains("code=")) { Uri uri = Uri.parse(url); String authCode = uri.getQueryParameter("code"); mView.authCodeObtained(authCode); isConsumed = true; } return isConsumed;
}And I have this Mockito test method:
@Test
public void onShouldOverrideUrlLoadingOnAuthCodeObtained(){ String code = "someCode"; boolean isConsumed = mPresenter.onShouldOverrideUrlLoading("" + code); verify(mView, times(1)).authCodeObtained(code); assertEquals(isConsumed, true);
}But it seems once the code runs and it reaches Uri.parse(url), I get a null pointer. What am I missing? In production this works perfectly. Only when testing, Uri.parse() returns null.
Thank you!
411 Answers
Eventually I used PowerMock on top of Mockito to mock the Uri class. Use these dependecies to add it:
'org.powermock:powermock-api-mockito:1.4.12'
'org.powermock:powermock-module-junit4:1.6.2'Read about it here.
It enables you to mock static methods, private methods, final classes and more. In my test method I used:
PowerMockito.mockStatic(Uri.class);
Uri uri = mock(Uri.class);
PowerMockito.when(Uri.class, "parse", anyString()).thenReturn(uri);This passed the line in the actual code and returned a Mocked Uri object so the test could move forward. Make sure to add:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Uri.class})As annotations on top of the test class name.
Using Robolectric to tackle this issue is also a valid technique.
Hope this helps some what.
0I got around this by using the Roboelectric test runner.
@RunWith(RobolectricGradleTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(19))
class WeekPageViewModelConverterTests {
...
} 1 The problem here is the fact that android.jar file that is used to run unit tests does not contain any actual code.
This way android.net.Uri will return null by default and using
android { // ... testOptions { unitTests.returnDefaultValues = true }
}will not solve the issue for this case, since default return value for method still null
The solution for me was to use the Unmock plugin which sole purpose is to unmock some of the classes/packages from android.jar and give the full implementation for those.
In this specific case, simply add the snippet to you build.gradle file
apply plugin: 'de.mobilej.unmock' //...
unMock { keepStartingWith "android.net.Uri"
} 1 I solved the problem by running the test suite with "@RunWith(RobolectricTestRunner::class)"
2The problem probably lies in Uri class, as the code being tested is static code (Uri.parse(...)), Uri is probably badly initializing in the test environment. Note there's two uri classes in the Android SDK :
I'm not an Android developer, but you may want to check the test environment.
2I had a similar problem doing tests with android.net.Uri class.
I've solved the problem moving the test from the 'test' folder to 'androidTest' and running it in the device. In the device, the Uri class behaves normally.
I was able to use android.net.Uri using AndroidJUnit4 runner as follows
import androidx.core.net.toUri
import androidx.test.ext.junit.runners.AndroidJUnit4
...
@RunWith(AndroidJUnit4::class)
class FileTest { @get:Rule(order = 0) val tempFolder = TemporaryFolder() private lateinit var tempFile: File @Before fun setup() { tempFile = tempFolder.newFile() } @Test fun test_fileUri_not_null() { assertTrue(tempFile.exists()) assertNotNull(tempFile.toUri()) }
}Above test is written under tests directory
For anyone else looking, you can mock a Uri using mockito.
In kotlin:
mockkStatic(Uri::class)
every { Uri.parse(any()) } returns mockk()Here is a link with more info on mocking static methods.
If you use the android.net.Uri class, then you need to run your tests with Robolectric () on top of Mockito
The only solution that worked for me was the following, while using powermockito.
PowerMockito.mockStatic(Uri.class);
Uri uri = mock(Uri.class);
when(uri.getLastPathSegment()).thenReturn("path");
when(Uri.parse(anyString())).thenAnswer((Answer<Uri>) invocation -> uri);In this, we can set custom values to be returned by respective functions of uri object.
The problem is that the android jar that's used during unit test runs doesn't have any real implementations. We can do the following to use a proper implementation of Uri:
Add
"com.github.bjoernq:unmockplugin:x.x.x"to theclasspathin the project's or module'sbuildScriptAdd
de.mobilej.unmockin thepluginblock as anidAdd the following task:
unmock { keep("android.net.Uri") }
Run your unit test. Uri methods should start working.