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

Refactoring android tests to use utils/android (pt5). #397

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions formula-android-tests/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,5 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name=".test.TestFragmentActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ import com.instacart.formula.android.events.FragmentLifecycleEvent
import com.instacart.formula.test.TestBackCallbackRenderModel
import com.instacart.testutils.android.TestKey
import com.instacart.formula.test.TestKeyWithId
import com.instacart.formula.test.TestFragmentActivity
import com.instacart.formula.test.TestLifecycleKey
import com.instacart.testutils.android.HeadlessFragment
import com.instacart.testutils.android.TestFormulaActivity
import com.instacart.testutils.android.activity
import com.instacart.testutils.android.get
import com.instacart.testutils.android.showFragment
import com.jakewharton.rxrelay3.PublishRelay
import io.reactivex.rxjava3.core.Observable
Expand All @@ -31,7 +29,6 @@ import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.robolectric.Shadows
import org.robolectric.annotation.LooperMode
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
Expand All @@ -44,6 +41,8 @@ class FormulaFragmentTest {
private var updateThreads = linkedSetOf<Thread>()
private val errors = mutableListOf<Throwable>()
private val fragmentLifecycleEvents = mutableListOf<FragmentLifecycleEvent>()
private val renderCalls = mutableListOf<Pair<FragmentKey, *>>()

private val formulaRule = TestFormulaRule(
initFormula = { app ->
val environment = FragmentEnvironment(
Expand All @@ -52,17 +51,25 @@ class FormulaFragmentTest {
}
)
FormulaAndroid.init(app, environment) {
activity<TestFragmentActivity> {
activity<TestFormulaActivity> {
ActivityStore(
onRenderFragmentState = { a, state ->
lastState = state

updateThreads.add(Thread.currentThread())
},
fragmentStore = FragmentStore.init {
bind(TestFeatureFactory<TestKey> { stateChanges(it) })
bind(
featureFactory = TestFeatureFactory<TestKey>(
render = { key, value ->
renderCalls.add(key to value)
},
state = { stateChanges(it) }
)
)
bind(TestFeatureFactory<TestKeyWithId>(
applyOutput = { output ->
render = { key, output ->
renderCalls.add(key to output)
if (output == "crash") {
throw IllegalStateException("crashing")
}
Expand All @@ -87,10 +94,10 @@ class FormulaFragmentTest {
}
)

private val activityRule = ActivityScenarioRule(TestFragmentActivity::class.java)
private val activityRule = ActivityScenarioRule(TestFormulaActivity::class.java)

@get:Rule val rule = RuleChain.outerRule(formulaRule).around(activityRule)
lateinit var scenario: ActivityScenario<TestFragmentActivity>
lateinit var scenario: ActivityScenario<TestFormulaActivity>

@Before fun setup() {
scenario = activityRule.scenario
Expand Down Expand Up @@ -136,35 +143,31 @@ class FormulaFragmentTest {
}

@Test fun `render model is passed to visible fragment`() {
val activity = activity()
sendStateUpdate(TestKey(), "update")
assertThat(activity.renderCalls).containsExactly(TestKey() to "update").inOrder()
assertThat(renderCalls).containsExactly(TestKey() to "update").inOrder()
}

@Test fun `render model is not passed to not visible fragment`() {
navigateToTaskDetail()

val activity = activity()
sendStateUpdate(TestKey(), "update")
assertThat(activity.renderCalls).isEqualTo(emptyList<Any>())
assertThat(renderCalls).isEqualTo(emptyList<Any>())
}

@Test fun `visible fragments are updated when navigating`() {
navigateToTaskDetail()

val contract = TestKeyWithId(1)

val activity = activity()
sendStateUpdate(contract, "update")
assertThat(activity.renderCalls).containsExactly(contract to "update").inOrder()
assertThat(renderCalls).containsExactly(contract to "update").inOrder()

navigateBack()

sendStateUpdate(contract, "update-two")
assertThat(activity.renderCalls).containsExactly(contract to "update").inOrder()
assertThat(renderCalls).containsExactly(contract to "update").inOrder()
}

@LooperMode(LooperMode.Mode.LEGACY)
@Test fun `delegates back press to current render model`() {
navigateToTaskDetail()

Expand Down Expand Up @@ -318,19 +321,18 @@ class FormulaFragmentTest {
val key = TestKeyWithId(1)
navigateToTaskDetail(id = key.id)

val activity = activity()
sendStateUpdate(key, "crash")
assertThat(activity.renderCalls).isNotEmpty()
assertThat(renderCalls).isNotEmpty()

assertThat(errors).hasSize(1)
}

@Test
fun toStringContainsTagAndKey() {
val fragment = FormulaFragment.newInstance(TestLifecycleKey())
val fragment = FormulaFragment.newInstance(TestKey())
val toStringValue = fragment.toString()
assertThat(toStringValue).isEqualTo(
"test-lifecycle -> TestLifecycleKey(tag=test-lifecycle)"
"test key -> TestKey(tag=test key)"
)
}

Expand All @@ -350,20 +352,18 @@ class FormulaFragmentTest {
}
}

private fun activity(): TestFragmentActivity {
private fun activity(): TestFormulaActivity {
return scenario.activity()
}

private fun activeContracts(): List<FragmentKey> {
return scenario.get {
lastState!!.activeIds.map { it.key }
}
return lastState!!.activeIds.map { it.key }
}

private fun assertVisibleContract(contract: FragmentKey) {
assertNoDuplicates(contract)
// TODO: would be best to test visibleState() however `FragmentFlowState.states` is empty
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO still valid?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, just checked. The tests needs a refactor so that it could rely on visibleOutput

assertThat(scenario.get { lastState?.visibleIds?.lastOrNull()?.key }).isEqualTo(contract)
assertThat(lastState?.visibleIds?.lastOrNull()?.key).isEqualTo(contract)
}

private fun assertNoDuplicates(contract: FragmentKey) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@ package com.instacart.formula
import com.instacart.formula.android.Feature
import com.instacart.formula.android.FeatureFactory
import com.instacart.formula.android.FragmentKey
import com.instacart.formula.test.TestFragmentActivity
import com.instacart.testutils.android.TestViewFactory
import io.reactivex.rxjava3.core.Observable

class TestFeatureFactory<Key : FragmentKey>(
private val applyOutput: (Any) -> Unit = {},
private val render: (FragmentKey, Any) -> Unit = { _, _ -> },
private val state: (Key) -> Observable<Any>,

) : FeatureFactory<Unit, Key> {
override fun initialize(dependencies: Unit, key: Key): Feature {
return Feature(
state = state(key),
viewFactory = TestViewFactory { view, value ->
(view.context as TestFragmentActivity).renderCalls.add(Pair(key, value))
applyOutput(value)
viewFactory = TestViewFactory { _, value ->
render(key, value)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.google.common.truth.Truth.assertThat
import com.instacart.formula.FormulaAndroid
import com.instacart.formula.android.events.ActivityResult
import com.instacart.formula.android.test.runActivityUpdateTest
import com.instacart.formula.android.test.runFeatureFactoryLifecycleTest
import com.instacart.testutils.android.TestActivity
import com.instacart.testutils.android.TestFormulaActivity
import com.instacart.testutils.android.TestFragmentActivity
Expand All @@ -27,7 +28,6 @@ class FormulaAndroidTest {

@Test
fun `crashes if initialized twice`() {

try {
val result = runCatching {
val context = ApplicationProvider.getApplicationContext<Application>()
Expand Down Expand Up @@ -180,4 +180,43 @@ class FormulaAndroidTest {
)
}
}

@Test
fun `feature factory lifecycle events`() {
runFeatureFactoryLifecycleTest {
assertThat(lifecycleCallback.hasOnViewCreated).isTrue()
assertThat(lifecycleCallback.hasOnActivityCreated).isTrue()
assertThat(lifecycleCallback.hasOnStart).isTrue()
assertThat(lifecycleCallback.hasOnResume).isTrue()

scenario.close()

assertThat(lifecycleCallback.hasOnPauseEvent).isTrue()
assertThat(lifecycleCallback.hasOnStop).isTrue()
assertThat(lifecycleCallback.hasOnDestroyView).isTrue()
}
}

@Test
fun `feature factory save instance event`() {
runFeatureFactoryLifecycleTest {
assertThat(lifecycleCallback.hasOnSaveInstanceState).isFalse()
scenario.recreate()
assertThat(lifecycleCallback.hasOnSaveInstanceState).isTrue()
}
}

@Test
fun `feature factory low memory event`() {
runFeatureFactoryLifecycleTest {
scenario.onActivity {
val fragment = it.supportFragmentManager.fragments
.filterIsInstance<FormulaFragment>()
.first()

fragment.onLowMemory()
}
assertThat(lifecycleCallback.hasCalledLowMemory).isTrue()
}
}
}
Loading
Loading