Domain layer testing

今天会为上一篇所写的两个 use case 加上 unit test。

GetLinesAndStationsUseCaseImplTest

这个 test 其实很简单,因为本身就是直接把 EtaRepository 拿到的 Map return 出去,所以 unit test 我们只需 mock EtaRepositorygetLinesAndStations 然後检查 use case return 出来的 map 是不是跟我们 mock 出来的 getLinesAndStations return value 是否一致即可。

private val DUMMY_DATA = mapOf(
    Line.AEL to setOf(Station.HOK, Station.KOW),
    Line.TKL to setOf(Station.NOP, Station.YAT, Station.LHP),
)

class GetLinesAndStationsUseCaseImplTest {

    private lateinit var useCase: GetLinesAndStationsUseCase

    @MockK
    private lateinit var repository: EtaRepository

    @Before
    fun setUp() {
        MockKAnnotations.init(this)
        useCase = GetLinesAndStationsUseCaseImpl(repository)
        every { repository.getLinesAndStations() } returns DUMMY_DATA

    }

    @Test
    fun invoke() {
        expectThat(useCase()).isEqualTo(DUMMY_DATA)
    }
}

GetEtaUseCaseImplTest

另一个 unit test 是关於车站班次,基本上都是左手交右手(把 EtaRepository return 出来的东西直接 return 给 ViewModel 用),唯独是在 EtaResult.Success 时我们会再排序。所以我们这次测试的重点会落在 EtaResult.Success 的情景。首先是测试按行车方向排序:

class GetEtaUseCaseImplTest {

    private lateinit var useCase: GetEtaUseCase

    @MockK
    private lateinit var repository: EtaRepository

    @Before
    fun setUp() {
        MockKAnnotations.init(this)
        useCase = GetEtaUseCaseImpl(repository)
    }

    @Test
    fun `success, sort by direction`() = runBlockingTest {
        coEvery {
            repository.getEta(
                Language.ENGLISH,
                Line.TCL,
                Station.OLY
            )
        } returns EtaResult.Success(
            schedule = listOf(
                EtaResult.Success.Eta(
                    direction = EtaResult.Success.Eta.Direction.DOWN,
                    platform = "1",
                    time = Instant.ofEpochSecond(1630700001),
                    destination = Station.HOK,
                    sequence = 2,
                ),
                EtaResult.Success.Eta(
                    direction = EtaResult.Success.Eta.Direction.UP,
                    platform = "1",
                    time = Instant.ofEpochSecond(1630700002),
                    destination = Station.TSY,
                    sequence = 2,
                ),
                EtaResult.Success.Eta(
                    direction = EtaResult.Success.Eta.Direction.UP,
                    platform = "1",
                    time = Instant.ofEpochSecond(1630700002),
                    destination = Station.TUC,
                    sequence = 1,
                ),
                EtaResult.Success.Eta(
                    direction = EtaResult.Success.Eta.Direction.DOWN,
                    platform = "1",
                    time = Instant.ofEpochSecond(1630700004),
                    destination = Station.KOW,
                    sequence = 1,
                ),
            ),
        )
        val result =
            useCase.invoke(Language.ENGLISH, Line.TCL, Station.OLY, GetEtaUseCase.SortBy.DIRECTION)
        expectThat(result).isA<EtaResult.Success>().get(EtaResult.Success::schedule).hasSize(4)
            .and {
                get(0).get(EtaResult.Success.Eta::destination).isEqualTo(Station.TUC)
                get(1).get(EtaResult.Success.Eta::destination).isEqualTo(Station.TSY)
                get(2).get(EtaResult.Success.Eta::destination).isEqualTo(Station.KOW)
                get(3).get(EtaResult.Success.Eta::destination).isEqualTo(Station.HOK)
            }
    }
}

这次我们偷懒,我们把四笔班次的目的地 destination 都设定成不同车站,然後只检查车站就知道顺序是否正确。第二和第三笔班次是用来测试当时间相同时会不会按序号 sequence 排列。

另一个 test case 是测试按时间排序。跟刚才的 success, sort by direction test case 一样,我们都是借 destination 判断排序是否正确。留意第一及第二笔的时间 time 都是相同,目的是检查结果是否有按序号 sequence 排列。

@Test
fun `success, sort by time`() = runBlockingTest {
    coEvery {
        repository.getEta(
            Language.ENGLISH,
            Line.TCL,
            Station.OLY
        )
    } returns EtaResult.Success(
        schedule = listOf(
            EtaResult.Success.Eta(
                direction = EtaResult.Success.Eta.Direction.DOWN,
                platform = "1",
                time = Instant.ofEpochSecond(1630700001),
                destination = Station.HOK,
                sequence = 2,
            ),
            EtaResult.Success.Eta(
                direction = EtaResult.Success.Eta.Direction.UP,
                platform = "1",
                time = Instant.ofEpochSecond(1630700001),
                destination = Station.TUC,
                sequence = 1,
            ),
            EtaResult.Success.Eta(
                direction = EtaResult.Success.Eta.Direction.UP,
                platform = "1",
                time = Instant.ofEpochSecond(1630700004),
                destination = Station.TSY,
                sequence = 2,
            ),
            EtaResult.Success.Eta(
                direction = EtaResult.Success.Eta.Direction.DOWN,
                platform = "1",
                time = Instant.ofEpochSecond(1630700003),
                destination = Station.KOW,
                sequence = 1,
            ),
        ),
    )
    val result =
        useCase.invoke(Language.ENGLISH, Line.TCL, Station.OLY, GetEtaUseCase.SortBy.TIME)
    expectThat(result).isA<EtaResult.Success>().get(EtaResult.Success::schedule).hasSize(4)
        .and {
            get(0).get(EtaResult.Success.Eta::destination).isEqualTo(Station.TUC)
            get(1).get(EtaResult.Success.Eta::destination).isEqualTo(Station.HOK)
            get(2).get(EtaResult.Success.Eta::destination).isEqualTo(Station.KOW)
            get(3).get(EtaResult.Success.Eta::destination).isEqualTo(Station.TSY)
        }
}

除了 EtaResult.Success 外,我们还会为其他情况写 test case,下面是 EtaResult.Incident 的 test case:

@Test
fun incident() = runBlockingTest {
    val incident = EtaResult.Incident("Out of order!", "http://example.com")
    coEvery {
        repository.getEta(
            Language.ENGLISH,
            Line.TCL,
            Station.OLY
        )
    } returns incident
    val result =
        useCase.invoke(Language.ENGLISH, Line.TCL, Station.OLY, GetEtaUseCase.SortBy.DIRECTION)
    expectThat(result).isEqualTo(incident)
}

基本上都是比对 return value 是不是跟 EtaRepository return 出来的一样,其余的 test case 都是同样写法,所以我就不逐一贴出来。完整的 test class 可以到 GitHub repo 查看。

下一篇我们会开始做 presentation layer。


<<:  Proxmox VE 安装容器:Rocky Linux 8.4 及其它应用 (WordPress, Nextcloud, Odoo)

>>:  [前端暴龙机,Vue2.x 进化 Vue3 ] Day22. Vue 旅游小帮手(完成)

[Q&A] 01民营企业是否要遵循资安法?

最近一年内被问过的问题,在此不具名回复,以下以废宅的自问自答模式展开~~~ Q: 民营企业是否要遵循...

[Day25] 实作 - 动画篇2

先在UI上做一个事件技能的锚点 修改一下ActionBattle_Action 修改一下算技能的距离...

第二天:什麽是 CI/CD?

虽然一讲到敏捷开发、DevOps 时就很常听到 CI/CD 这些词汇,不过到底什麽是 CI?又什麽是...

铁人赛 Day8 -- 一定要知道的 CSS (五) - align & 如何使 ''Div'' 区块 置中对齐

前言 align 主要是以交错轴为主,而昨天的 ustify-content 则是以主轴为主 ali...

Day 12 | 魔术方块AR游戏开发Part1 - 魔术方块建立

在上一篇文章中为各位介绍ARFoundation/ARCore,今天我们要来制作魔术方块AR游戏。 ...