docs: Task 12 用例三补鉴别力、previewPoints 补 step 参数(ledger R34)

This commit is contained in:
2026-09-10 12:41:17 +08:00
parent cd6d53a3b6
commit a18523f032
@@ -3897,6 +3897,7 @@ import kotlinx.coroutines.test.setMain
import org.junit.After import org.junit.After
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -3952,16 +3953,31 @@ class MetricDetailViewModelTest {
} }
@Test @Test
fun `构造时传入的 metric 被原样带进状态`() = runTest(dispatcher) { fun `构造时传入的 metric 既进状态、也决定了查询哪一组数据`() = runTest(dispatcher) {
// 名字就叫它断言的事,不要写成「标题映射不同」——`metricTitleRes` 是 @Composable, // 名字就叫它断言的事,不要写成「标题映射不同」——`metricTitleRes` 是 @Composable,
// JVM 测试里根本调不了,那样命名会让人以为这里验了标题映射(其实没有, // JVM 测试里根本调不了,那样命名会让人以为这里验了标题映射(其实没有,
// 映射本身由 `when` 的穷尽性在编译期保证)。本用例只锁「metric 被原样带进状态」。 // 映射本身由 `when` 的穷尽性在编译期保证)。
val viewModel = MetricDetailViewModel(MetricType.SLEEP, MockHealthDataRepository()) val viewModel = MetricDetailViewModel(MetricType.SLEEP, MockHealthDataRepository())
backgroundScope.launch { viewModel.uiState.collect {} } backgroundScope.launch { viewModel.uiState.collect {} }
advanceUntilIdle() advanceUntilIdle()
assertEquals(MetricType.SLEEP, viewModel.uiState.value.metric) val state = viewModel.uiState.value
// ⚠️ 这一条**单独放在这里是没有鉴别力的**:`metric` 本来就在 stateIn 的 initialValue 里
// (`MetricDetailUiState(loading = true, metric = metric, …)`),数据管线从不发射时它照样绿。
// 留着它是因为它锁住了「构造参数被原样带进状态」,但**真正干活的是下面两条**(ledger R34)。
assertEquals(MetricType.SLEEP, state.metric)
// 鉴别力来源一:排除「管线从不发射」。此时 points 停在 initialValue 的 emptyList()。
assertEquals(7, state.points.size)
// 鉴别力来源二:把构造参数与**实际查询到的数据**绑起来。
// `MetricDetailViewModel` 里调的是 `observeMetricHistory(metric, currentRange)`;
// 若这一行被错传成 `MetricType.STEP`(3000~12000),或参数索引错位,下面的区间断言就会挂。
// 区间取自 MockDataProvider.valueRange,务必与那边保持一致:
// STEP 3000~12000 / HEART_RATE 58~96 / SLEEP 330~510 / …
// ⚠️ 不要只写 `all {}` —— **空列表上 `all` 恒真**,那样会留出一个洞;
// 上面的 size 断言就是用来堵这个洞的,两条必须同时在。
assertTrue(state.points.all { it.value in 330.0..510.0 })
} }
} }
``` ```
@@ -4390,13 +4406,17 @@ private fun StatLine(label: String, value: Double, modifier: Modifier = Modifier
private fun List<Double>.averageOrZero(): Double = if (isEmpty()) 0.0 else average() private fun List<Double>.averageOrZero(): Double = if (isEmpty()) 0.0 else average()
/** /**
* Preview 用的样例数据:7 个点、每天一个、值从 `base` 起小幅递增。 * Preview 用的样例数据:7 个点、每天一个,值从 `base` 起按 `step` 递增。
* 取值贴近区间下限,好让柱状图有高低差、统计摘要的三行数字不相等(都相等就看不出渲染问题)。 *
* `step` 必须与 `base` 同量级、且让整段值落在该指标的合理区间内,否则柱状图会**看起来是平的**
* (`MetricBarChart` 按 `value / maxValue` 定高,7 根柱子之间只差 1% 就等于没有高低差,
* 那正是预览最该暴露的渲染问题)。睡眠用 `base` 低、`step` 小;步数用 `base` 大、`step` 也大。
* 统计摘要的三行数字也依赖它不相等 —— 都相等就看不出渲染问题。
*/ */
private fun previewPoints(base: Double): List<MetricPoint> { private fun previewPoints(base: Double, step: Double): List<MetricPoint> {
val dayMillis = 24L * 60 * 60 * 1000 val dayMillis = 24L * 60 * 60 * 1000
val start = 1_700_000_000_000L val start = 1_700_000_000_000L
return List(7) { index -> MetricPoint(timestamp = start + index * dayMillis, value = base + index * 12) } return List(7) { index -> MetricPoint(timestamp = start + index * dayMillis, value = base + index * step) }
} }
/** /**
@@ -4412,7 +4432,8 @@ private fun MetricDetailContentSleepPreview() {
loading = false, loading = false,
metric = MetricType.SLEEP, metric = MetricType.SLEEP,
range = DateRange.WEEK, range = DateRange.WEEK,
points = previewPoints(base = 420.0), // 睡眠时长(分钟),与 Mock 的 330~510 同量级 // 睡眠时长(分钟):360 → 504,落在 Mock 的 330~510 内,柱子有可见高低差
points = previewPoints(base = 360.0, step = 24.0),
), ),
onBack = {}, onBack = {},
onSelectRange = {}, onSelectRange = {},
@@ -4433,7 +4454,10 @@ private fun MetricDetailContentStepsPreview() {
loading = false, loading = false,
metric = MetricType.STEP, metric = MetricType.STEP,
range = DateRange.WEEK, range = DateRange.WEEK,
points = previewPoints(base = 7500.0), // 步数,与 Mock 的 3000~12000 同量级 // 步数:5200 → 11720,落在 Mock 的 3000~12000 内;
// step 必须够大 —— 若像旧稿那样 base 7500 / step 12,7 根柱子全在 99%~100% 高度,
// 预览看起来就是一排等高的方块,等于白预览(ledger R34)
points = previewPoints(base = 5200.0, step = 1000.0),
), ),
onBack = {}, onBack = {},
onSelectRange = {}, onSelectRange = {},