Alon Minski
Alon Minski

Reputation: 1581

Mockito Uri.parse always returns null

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

Answers (12)

ultra.deep
ultra.deep

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

NSaran
NSaran

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

Mira_Cole
Mira_Cole

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

Ujjwal Kumar Maharana
Ujjwal Kumar Maharana

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

Kshitij Patil
Kshitij Patil

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

prago
prago

Reputation: 5423

I solved the problem by running the test suite with "@RunWith(RobolectricTestRunner::class)"

Upvotes: 3

Felipe Conde
Felipe Conde

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

iuri
iuri

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

Brice Pollock
Brice Pollock

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

Alon Minski
Alon Minski

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

VinceFR
VinceFR

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

bric3
bric3

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

Related Questions