Skip to content

Commit

Permalink
Refactoring android tests to use utils/android (pt5). (#397)
Browse files Browse the repository at this point in the history
  • Loading branch information
Laimiux authored Sep 23, 2024
1 parent feeac6c commit 98b71fb
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 149 deletions.
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
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

0 comments on commit 98b71fb

Please sign in to comment.