今天会继续写 EtaResponseMapperTest
。我们示范的 test case 是正常输出班次的情景。首先是准备 response:
val response = mockHttpResponse(
statusCode = HttpStatusCode.OK,
etaResponse = EtaResponse(
status = EtaResponse.STATUS_NORMAL,
message = "successful",
isDelay = EtaResponse.IS_DELAY_FALSE,
data = mapOf(
"TKL-TKO" to EtaResponse.Data(
up = listOf(
EtaResponse.Eta(
plat = "1",
time = "2020-01-11 14:28:00",
dest = "POA",
seq = "1",
),
EtaResponse.Eta(
plat = "1",
time = "2020-01-11 14:36:00",
dest = "LHP",
seq = "2",
),
),
down = listOf(
EtaResponse.Eta(
plat = "2",
time = "2020-01-11",
dest = "XXX",
seq = "",
),
),
),
),
),
)
在正常的情况下 server 会提供上行及下行的班次。在我们的实作中,上下行其实是用相同方法处理,那我们就在上行的 array 放一些正常的内容然後在下行放有问题的内容,看看我们的 mapper 能否正常处理。
接着就是 assertion 的部分。我们先检查输出的结果是不是 EtaResult.Success
,然後用 and
开了一个 lambda。
expectThat(mapper.map(response)).isA<EtaResult.Success>().and {
// 入面的 this 就变了 AssertionBuilder<EtaResult.Success>
// 这样就可以继续深入检查 EtaResult.Success 的 property
}
之後我们就会按照 EtaResult.Success
的每一个 property 检查。它只有一个 property schedule
,我们再可以开另一层的 lambda 检查 List<EtaResult.Success.Eta>
入面的内容。
针对 collection 的部分,Strikt 提供了几个 method 方便我们做 assertion:
isNotEmpty()
检查 collection 是不是空hasSize(3)
检查 collection 是不是有 3 个元素get(2)
拿 index 第 2 的元素first()
就是上面 get
的特别版,只取第一个元素containsExactly(1, 2, 3)
collection 入面必须要有这些元素,而且要按同样次序出现containsExactlyInAnyOrder(1, 2, 3)
和上面差不多,只是没规定先後次序更多的用法可以参考 Strikt 网站。之後我们可以再驳 and
来进入那一个元素作检查。一般我们都是会先用 get
取得那个 property 的 AssertionBuilder
然後再接驳那些 isEqualTo
、isNull
之类的 assertion。留意这个 get
不是 get(0)
那个,parameter 用数字是用来取得 collection 的某一个元素;但如果 parameter 是用 property/method reference 或者 lambda 就是用来取得那个 property。
以下是 property/method reference 和 lambda 写法例子:
val subject: EtaResult.Success.Eta = EtaResult.Success.Eta(...)
expectThat(subject) {
// property/method reference
get(EtaResult.Success.Eta::platform).isEqualTo("1")
// lambda
get { platform }.isEqualTo("1")
}
两者效果都是一样,但 Strikt 网站指出用 lambda 写法效能上比用 property/method reference 差。所以如果可以的话都尽量用 property/method reference。
以下是整个 assertion 的部分:
expectThat(mapper.map(response)).isA<EtaResult.Success>().and {
get(EtaResult.Success::schedule).isNotEmpty().and {
get(0).and {
get(EtaResult.Success.Eta::direction).isEqualTo(EtaResult.Success.Eta.Direction.UP)
get(EtaResult.Success.Eta::platform).isEqualTo("1")
get(EtaResult.Success.Eta::time).isEqualTo(
ZonedDateTime.of(
2020, 1, 11, 14, 28, 0, 0,
DEFAULT_TIMEZONE
).toInstant()
)
get(EtaResult.Success.Eta::destination).isEqualTo(Station.POA)
get(EtaResult.Success.Eta::sequence).isEqualTo(1)
}
get(1).and {
get(EtaResult.Success.Eta::direction).isEqualTo(EtaResult.Success.Eta.Direction.UP)
get(EtaResult.Success.Eta::platform).isEqualTo("1")
get(EtaResult.Success.Eta::time).isEqualTo(
ZonedDateTime.of(
2020, 1, 11, 14, 36, 0, 0,
DEFAULT_TIMEZONE
).toInstant()
)
get(EtaResult.Success.Eta::destination).isEqualTo(Station.LHP)
get(EtaResult.Success.Eta::sequence).isEqualTo(2)
}
get(2).and {
get(EtaResult.Success.Eta::direction).isEqualTo(EtaResult.Success.Eta.Direction.DOWN)
get(EtaResult.Success.Eta::platform).isEqualTo("2")
get(EtaResult.Success.Eta::time).isEqualTo(Instant.EPOCH)
get(EtaResult.Success.Eta::destination).isEqualTo(Station.UNKNOWN)
get(EtaResult.Success.Eta::sequence).isEqualTo(0)
}
}
}
留意因为 Instant
有 override equals
和 hashCode
,还有是 immutable,所以我们可以直接建构一个新的 Instant
跟输出做比较,如果那个 class 没有正确地 override equals
和 hashCode
的话,还是要逐个 property call getter 检查比较安全。
你或许会问为甚麽我们不直接建构一个塞好我们期望的内容的 EtaResult.Success
object 然後直接 isEqualTo
做比对。这是因为我想善用 Strikt 这类 assertion library 的优势。Strikt 这类 assertion library 如果 assertion 出问题的话它会输出详细的错误讯息让你看。
@Test
fun incident() = runBlockingTest {
val response = mockHttpResponse(
statusCode = HttpStatusCode.OK,
etaResponse = EtaResponse(
status = EtaResponse.STATUS_ERROR_OR_ALERT,
message = "Special train service arrangements are now in place on this line.",
url = "https://www.mtr.com.hk",
),
)
expectThat(mapper.map(response)).isA<EtaResult.Incident>().and {
get(EtaResult.Incident::message).isEqualTo("Special train service arrangements are now in place on this line.")
get(EtaResult.Incident::url).isEqualTo("https://www.mtr.com.hk/alert/alert_title_wap.html")
}
}
▼ Expect that Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk):
✓ is an instance of net.swiftzer.etademo.domain.EtaResult$Incident
▼ value of property message:
✓ is equal to "Special train service arrangements are now in place on this line."
▼ value of property url:
✗ is equal to "https://www.mtr.com.hk/alert/alert_title_wap.html"
found "https://www.mtr.com.hk"
如果直接用 isEqualTo
的话,那 assertion 会写成:
expectThat(mapper.map(response)).isA<EtaResult.Incident>().isEqualTo(
EtaResult.Incident(
message = "Special train service arrangements are now in place on this line.",
url = "https://www.mtr.com.hk/alert/alert_title_wap.html",
)
)
之後输出的错误讯息都是把那个 class 的 toString
放给你自己看,但 property 一多你就很难检查:
▼ Expect that Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk):
✓ is an instance of net.swiftzer.etademo.domain.EtaResult$Incident
✗ is equal to Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk/alert/alert_title_wap.html)
found Incident(message=Special train service arrangements are now in place on this line., url=https://www.mtr.com.hk)
EtaResponseMapperTest
的其他 test case 其实写法都大同小异,所以我就不逐一介绍。有兴趣的话可以直接去 GitHub 看 code。
下一篇会写 EtaRepositoryImplTest
。
在新增仪表板窗格前,我们先新增一张工作表,名称为「app总数」,我们选择栏位「F1」,度量选择「计数...
CFP是Call for Papers/Call for Proposals的缩写,中文可以称作是研...
-安全内核 一张图片胜过千言万语。访问控制矩阵可以被视为授权数据(权利和许可)的逻辑“存储库”,由...
逻辑运算子 MDN : https://developer.mozilla.org/zh-TW/do...
Virtual Judge ZeroJudge 题意 对每一列输入,输出各字元的 ASCII &a...