Reputation: 1581
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("http://localhost/?code=" + 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!
Upvotes: 23
Views: 36951
Reputation: 1809
I used real Uri
with Base64
Image data TestWatcher
and worked fine for me...
TestWatcher
Implementation :
class TestContentUriRule(
private val base64File :String = DEFAULT_BASE64_IMAGE,
private val filename:String = DEFAULT_FILENAME
) : TestWatcher() {
companion object {
const val DEFAULT_BASE64_IMAGE = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII="
const val DEFAULT_FILENAME = "image.jpg"
}
lateinit var file: File
lateinit var contentUri: Uri
override fun starting(description: Description?) {
super.starting(description)
val context = ApplicationProvider.getApplicationContext<Context>()
val decodedString: ByteArray = Base64.decode(base64File, Base64.DEFAULT)
val bitmap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString!!.size)
try {
file = File(context.cacheDir, filename)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, file.outputStream())
contentUri = Uri.fromFile(file)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun finished(description: Description?) {
val context = ApplicationProvider.getApplicationContext<Context>()
File(context.cacheDir, filename).delete()
super.finished(description)
}
}
and usage :
class CashReceiptPageTest : TraderAndroidTest() {
@get:Rule
val contentUriRule = TestContentUriRule()
@Test
fun sampleTest() {
// some codes ...
selectReceiptPicture(uri = contentUriRule.contentUri)
// some codes ...
}
}
Upvotes: 0
Reputation: 618
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 the classpath
in the project's or module's buildScript
Add de.mobilej.unmock
in the plugin
block as an id
Add the following task:
unmock { keep("android.net.Uri") }
Run your unit test. Uri methods should start working.
Upvotes: 0
Reputation: 783
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.
Upvotes: 3
Reputation: 279
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.
Upvotes: 0
Reputation: 162
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
Upvotes: 1
Reputation: 5423
I solved the problem by running the test suite with "@RunWith(RobolectricTestRunner::class)"
Upvotes: 3
Reputation: 2044
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"
}
Upvotes: 5
Reputation: 21
I 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.
Upvotes: 1
Reputation: 193
I got around this by using the Roboelectric test runner.
@RunWith(RobolectricGradleTestRunner::class)
@Config(constants = BuildConfig::class, sdk = intArrayOf(19))
class WeekPageViewModelConverterTests {
...
}
Upvotes: 8
Reputation: 1581
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.
Upvotes: 23
Reputation: 2571
If you use the android.net.Uri class, then you need to run your tests with Robolectric (http://robolectric.org) on top of Mockito
Upvotes: 0
Reputation: 42223
The 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.
Upvotes: 2