如果记录类型包括生成的字段,例如自动生成的id和时间戳,例如:
type UserCreated = {
Id: Guid
Name: string
CreationTime: Instant
}
针对此编写单元测试的最佳方法是什么?不可能简单地断言记录等于任何特定值,因为事先无法知道Id和CreationTime值是什么。
可能的解决方案:
什么是最好的方法呢?
答案 0 :(得分:3)
你并没有真正测试记录类型本身 - 它只是愚蠢的数据。您要测试的是对该数据进行操作的函数。
当一个函数纯粹时,它很容易测试。调用Guid.NewGuid()
和DateTime.Now
两者都是副作用,这是我假设你在这里对两个有问题的字段所做的。因此,将这两个位重构为您明确作为参数传递的函数将是我的书中的方法(您的要点2)。它会使代码更容易测试并且更加纯净(也许可读性稍差一点,我想你需要平衡它)。
这就是说 - 对于大多数涉及您的记录的测试用例,您应该在安排测试时知道这些字段的值。只有当您测试创建“从无”创建记录的函数时,您才能事先知道它们,并且在这种情况下,您只需检查Name
字段是否具有预期值(因此您的要点1必要时,将记录“按原样”比较。
编辑:我不知道你是否错过了,但另一种可能性是在测试项目中定义一个专用的比较函数,并确保'生成的'值是相同的在比较它们之前的两个记录中:
let compareWithoutGenerated (a: UserCreated) (b: UserCreated) =
a = { b with Id = a.Id; CreationTime = a.CreationTime }
然后:
WhateverUnit.Assert.True(compareWithoutGenerated a b)
显然这是丑陋的,但我会说这是公平的测试游戏。可以说有更好的方法,但至少,这个方法不会对您的生产代码造成测试造成的损害。
答案 1 :(得分:3)
我的方法确实是将函数传递给用户创建函数,该函数处理id和日期的生成。如果您需要传递太多参数,那么您可能需要重构您的设计。
这里是域层,例如:
// =================
// Domain
// =================
open System
type UserCreated = {
Id: Guid
Name: string
CreationTime: DateTime
}
// the generation functions are passed in
let createCompleteRecord generateGuid generateTime name =
{
Id = generateGuid()
Name = name // add validation?
CreationTime = generateTime()
}
在应用程序代码中,您使用部分应用程序烘焙生成器并创建有用的函数createRecord
// =================
// Application code
// =================
let autoGenerateGuid() = Guid.NewGuid()
let autoGenerateTime() = DateTime.UtcNow
// use partial application to get a useful version
let createRecord = createCompleteRecord autoGenerateGuid autoGenerateTime
let recForApp = createRecord "myname"
在测试代码中,您使用部分应用程序烘焙其他生成器并创建不同的函数createRecordForTesting
// =================
// Test code
// =================
let explicitGenerateGuid() = Guid.Empty
let explicitGenerateTime() = DateTime.MinValue
// use partial application to get a useful version
let createRecordForTesting = createCompleteRecord explicitGenerateGuid explicitGenerateTime
let recForTest = createRecordForTesting "myname"
Assert.AreEqual(Guid.Empty,recForTest.Id)
Assert.AreEqual("myname",recForTest.Name)
Assert.AreEqual(DateTime.MinValue,recForTest.CreationTime)
并且"生成"字段现在具有硬编码值,您也可以测试整个记录相等逻辑:
let recForTest1 = createRecordForTesting "myname"
let recForTest2 = createRecordForTesting "myname"
Assert.AreEqual(recForTest1,recForTest2)