How to do UI testing for kotlin file with dagger in android?

ぃ、小莉子 提交于 2020-01-02 07:23:14

问题


Below is my stack trace, I have gone through all the questions and answers on SO but can't find any solution

java.lang.IllegalStateException: Could not initialize plugin: interface 
org.mockito.plugins.MockMaker (alternate: null)
at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:74)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy6.isTypeMockable(Unknown Source)
at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29)
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:232)
at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:64)
at org.mockito.Mockito.mock(Mockito.java:1871)
at org.mockito.Mockito.mock(Mockito.java:1780)
at SplashActivityTest.init(SplashActivityTest.kt:126)
at java.lang.reflect.Method.invoke(Native Method) 
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lnet/bytebuddy/dynamic/loading/ClassInjector$UsingReflection;
at org.mockito.internal.creation.bytebuddy.SubclassInjectionLoader.<init>(SubclassInjectionLoader.java:28)
at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.<init>(SubclassByteBuddyMockMaker.java:33)
at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.<init>(ByteBuddyMockMaker.java:21)
at java.lang.Class.newInstance(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection" on path: DexPathList[[zip file "/system/framework/android.test.runner.jar", zip file "/system/framework/android.test.mock.jar", zip file "/data/app/com.test-Ceb6_iDz-8wl1a3HhgqEEg==/base.apk", zip file "/data/app/YwRi3yxfA1u5ckInmXjV-A==/base.apk"],nativeLibraryDirectories=[/data/app/test-Ceb6_iDz-8wl1a3HhgqEEg==/lib/x86, /data/app/YwRi3yxfA1u5ckInmXjV-A==/lib/x86, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)

Below is my splashActivityTest,

import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.AndroidJUnit4
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf
import org.hamcrest.TypeSafeMatcher
import org.hamcrest.core.IsInstanceOf
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@LargeTest
@RunWith(AndroidJUnit4::class)
class SplashActivityTest {

@Rule
@JvmField
var mActivityTestRule = ActivityTestRule(SplashActivity::class.java)
@Rule
@JvmField
val executorRule = TaskExecutorWithIdlingResourceRule()
@Rule
@JvmField
val countingAppExecutors = CountingAppExecutorsRule()
@Rule
@JvmField
val dataBindingIdlingResourceRule = DataBindingIdlingResourceRule(mActivityTestRule)

private lateinit var prefUtils: PrefUtils
private lateinit var navigationController: NavigationController

@Before
fun init() {
    prefUtils = mock()
    navigationController = mock()
}

@Test
fun splashActivityTest() {
    // Added a sleep statement to match the app's execution delay.
    // The recommended way to handle such scenarios is to use Espresso idling resources:
    // https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/index.html
    Thread.sleep(2000)

    val imageView = onView(
        allOf(
            withId(R.id.logo),
            childAtPosition(
                childAtPosition(
                    IsInstanceOf.instanceOf(android.widget.FrameLayout::class.java),
                    0
                ),
                0
            ),
            isDisplayed()
        )
    )
    imageView.check(matches(isDisplayed()))

    val frameLayout = onView(
        allOf(
            childAtPosition(
                childAtPosition(
                    withId(android.R.id.content),
                    0
                ),
                0
            ),
            isDisplayed()
        )
    )
    frameLayout.check(matches(isDisplayed()))

    val frameLayout2 = onView(
        allOf(
            childAtPosition(
                childAtPosition(
                    withId(android.R.id.content),
                    0
                ),
                0
            ),
            isDisplayed()
        )
    )
    frameLayout2.check(matches(isDisplayed()))
}

private fun childAtPosition(
    parentMatcher: Matcher<View>, position: Int
): Matcher<View> {

    return object : TypeSafeMatcher<View>() {
        override fun describeTo(description: Description) {
            description.appendText("Child at position $position in parent ")
            parentMatcher.describeTo(description)
        }

        public override fun matchesSafely(view: View): Boolean {
            val parent = view.parent
            return parent is ViewGroup && parentMatcher.matches(parent)
                    && view == parent.getChildAt(position)
        }
    }
  }
}

Actual SplashActivity

@OpenForTesting
class SplashActivity : BaseActivity() {

/**
 * Returns layout file ID
 * */
override fun layoutId() = R.layout.activity_splash

/**
 * this method gets called when this activity gets created
 * all tasks those need to be executed when this activity get created
 * */
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    /**
     * TO Load Gif in #ImageView
     */
    Glide.with(this).load(R.raw.new_loading_logo).into(logo)
    /**
     * Handles Timer of 2000 millSeconds to open another Activity
     * prefUtils.isUserLogin() == true -> Opens DashBoard as User is already Logged In
     * else -> Opens Log In Page
     */
    //*while UI test runs, Here when prefUtils.isUserLogin() gets executed See error log below*
    Handler().postDelayed({
        if (prefUtils.isUserLogin())
            navigationController.navigateToDashBoard(this)
        else
            navigationController.navigateToLogin(this)
        finish()
    }, 2000)
  }
}

This Activity extends BaseActivity which is having below lines, hence after launch of splash, my test goes failed and stats that lateinit var prefUtils has not been initialised, now for this i have used mocking but still getting java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null).

@Inject
lateinit var navigationController: NavigationController
@Inject
lateinit var prefUtils: PrefUtils
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory

I have added dependencies as below,

testImplementation "junit:junit:$junitVersion"
testImplementation "org.mockito:mockito-core:$mockito"
testImplementation "org.mockito:mockito-inline:$mockito"

Below error happens when i dont mock anything,

kotlin.UninitializedPropertyAccessException: lateinit property prefUtils has not been initialized
at BaseActivity.getPrefUtils(BaseActivity.kt:41)
at SplashActivity$onCreate$1.run(SplashActivity.kt:38)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Test running failed: Instrumentation run failed due to 'Process crashed.'

as baseActivity has below line @Inject lateinit var prefUtils: PrefUtils Hence for solving this error i am using Mock

Now I have tried power mock too but it failed on compile time !!, It states

Unresolved reference: powermock

on below line

@RunWith(PowerMockRunner::class)

回答1:


Here SplashActivity is inheriting BaseActivity hence whenever test runs, Injected variables remains uninitialized for test hence I found solution by changing below line

@Rule
@JvmField
var mActivityTestRule = ActivityTestRule(SplashActivity::class.java)

to

@Rule
@JvmField
var mActivityTestRule = ActivityTestRule(FakeLogInActivity::class.java)

Below is code for FakeLogInActivity::class.java

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.sextpanther.sp.R

/**
* Used for testing fragments inside a fake activity.
*/
class FakeLogInActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_login)
  }
}

Now test running successfully, but only test with snackbar view matcher with text gets failed.



来源:https://stackoverflow.com/questions/53880101/how-to-do-ui-testing-for-kotlin-file-with-dagger-in-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!