Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alert executes blocking in instrumented test #219

Open
fjkc opened this issue Apr 24, 2020 · 0 comments
Open

Alert executes blocking in instrumented test #219

fjkc opened this issue Apr 24, 2020 · 0 comments

Comments

@fjkc
Copy link

fjkc commented Apr 24, 2020

I am currently working on automating my tests by using instrumented tests with Espresso. In some test cases I want to verify the title/text of an alert, but there seems to be an issue with the alert. It blocks my code and therefore also my assertion from running as long as the alert is showing. The assertion runs just after the alert hides again. In my project, I am testing 2 different Fragments, in one, the alert does not execute synchronously as long as i don’t register a IdlingResource. In the other one, even if i dont register any IdlingResource, it doesnt work. (also the case in the example code below).

The output i am getting in Logcat is:

2020-04-24 11:30:44.816 29889-29889/? I/ViewInteraction: Performing 'single click' action on view with id: com.example.a:id/loginButton
...
2020-04-24 11:30:45.628 29889-29889/com.example.a D/Alert: Show Alert called
2020-04-24 11:30:49.965 29889-29889/com.example.a D/Alert: Alert hiding
2020-04-24 11:30:50.105 29889-29889/com.example.a D/RootViewPicker: No matching root available - waiting: 10ms for one to appear.

Here is some code with the according dependencies to reproduce it:
build.gradle (:app)

compileSdkVersion 28
minSdkVersion 21
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation("androidx.test:core:1.2.0")
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test:rules:1.2.0'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation("androidx.test.espresso:espresso-idling-resource:3.2.0")

    def androidxLifecycle = "2.0.0"
    implementation("androidx.lifecycle:lifecycle-extensions:$androidxLifecycle")
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
    implementation("com.tapadoo.android:alerter:5.1.2")
}

MainActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

MyFragment

class MyFragment : Fragment() {
    private lateinit var viewModel: MyViewModel
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.my_fragment, container, false)
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)

        viewModel.requestStatus.observe(this, Observer {
            showAlert(R.string.title, R.string.message)
            Log.d("Alert","Show Alert called")
        })

        loginButton.setOnClickListener {
            viewModel.login()
        }
    }
}

MyViewModel

class MyViewModel : ViewModel() {
    private val repo = SomeRepository()
    var requestStatus = MutableLiveData<String>()

    fun login() {
        viewModelScope.launch {
            repo.login()
            requestStatus.postValue("DONE")
        }
    }
}

SomeRepository

class SomeRepository {
    fun login() = notIdleWhile {
        // network request
        Thread.sleep(500L)
    }
}

NotIdleWhile

inline fun <T> notIdleWhile(block: () -> T): T {
    EspressoIdlingResource.increment()
    val result = try {
        block()
    } catch (t: Throwable) {
        EspressoIdlingResource.decrement()
        throw t
    }
    EspressoIdlingResource.decrement()
    return result
}

EspressoIdlingResource

object EspressoIdlingResource {
    val countingIdlingResource = CountingIdlingResource("RESOURCE")
    fun increment() {
        countingIdlingResource.increment()
    }
    fun decrement() {
        if (!countingIdlingResource.isIdleNow)
            countingIdlingResource.decrement()
    }
}

FragmentExtension

fun Fragment.showAlert(@StringRes title: Int, @StringRes message: Int) {
    if (Alerter.isShowing)
        return
    try {
        val alerter = Alerter.create(activity)
            .setTitle(title)
            .setText(message)
            .enableInfiniteDuration(false)
            .setBackgroundColorRes(R.color.colorPrimary)
            .setOnHideListener(OnHideAlertListener { Log.d("Alert","Alert hiding") })
        alerter.show()
    } catch (ex: Exception) {
        Log.e("Alert","Couldn't show the alert due to: ${ex.message}")
    }
}

InstrumentedTest

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @get:Rule
    val actRule = ActivityTestRule(MainActivity::class.java)
    @Before
    fun setUp() {
        /*IdlingRegistry.getInstance().register(
            EspressoIdlingResource.countingIdlingResource)*/

        actRule.activity.supportFragmentManager.beginTransaction()
            .replace(R.id.frag_container, MyFragment()).commit()
    }
    @After
    fun tearDown() {
        /*IdlingRegistry.getInstance().unregister(
            EspressoIdlingResource.countingIdlingResource)*/
    }
    @Test
    fun showsAlertAfterClick(){
        onView(withId(R.id.loginButton)).perform(click())

        onView(withId(com.tapadoo.alerter.R.id.tvText))
            .inRoot(withDecorView(hasDescendant(withId(com.tapadoo.alerter.R.id.tvText))))
            .check(matches(withText(R.string.message)))
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant