我想用模拟对象编写F#单元测试。我正在使用NUnit。 但不幸的是我找不到任何例子。
以下是受测试代码的示例:
type ICustomer = interface
abstract Id: int with get
abstract Name: string with get
abstract CalculateBalanceWithDiscount: decimal -> decimal
end
type Customer = class
val id: int
val name: string
val balance: decimal
new(id, name, balance) =
{id = id; name = name; balance = balance}
interface ICustomer with
member this.Id
with get () = this.id
member this.Name
with get () = this.name
member this.CalculateBalanceWithDiscount discount =
this.balance - (discount * this.balance)
end
end
答案 0 :(得分:8)
作为旁注,您可以使用隐式构造函数语法使您的类声明更好一些。您还可以简化只读属性,因为您可以省略with get()
:
// F# infers that the type is an interface
type ICustomer =
abstract Id : int
abstract Name : string
abstract CalculateBalanceWithDiscount : decimal -> decimal
// Parameters of the implicit constructor are autoamtically
// accessible in the body (they are stored as fields)
type Customer(id:int, name:string, balance:decimal) =
interface ICustomer with
member this.Id = id
member this.Name = name
member this.CalculateBalanceWithDiscount(discount) =
balance - (discount * balance)
关于测试 - 您是否有任何关于您要实现的目标的示例?我相信我们可以通过翻译C#中的代码来提供帮助。或者你想用模拟写什么样的测试?
一般来说,F#和函数式语言的优点在于,您通常可以在不使用任何模拟的情况下更轻松地测试代码。功能程序以不同的风格编写:
在函数式编程中,函数将所有的输入作为参数,它唯一能做的就是计算并返回一些结果。对于不可变对象类型的方法也是如此 - 它们不会修改任何对象的任何状态
模拟通常用于两个目的:
验证测试操作是否对引用对象的方法执行了某些调用,例如prod.Update(newPrice)
更新对象的状态。但是,在函数式编程中,该方法应该返回新状态作为结果 - 因此您不需要模拟对象。只需检查新返回的状态是否符合预期。
要加载创建应用程序的虚假组件,例如,而不是从数据库加载数据。同样,纯函数函数应该将其所有输入作为参数。这意味着您不需要创建模拟对象 - 您只需使用一些测试数据作为参数调用该函数(而不是从数据库加载数据)。
总之,这意味着在一个设计良好的函数程序中,您应该能够将所有单元测试编写为检查,以验证某些函数返回预期参数的预期结果。当然,这在F#中并不严格,因为您可能需要与其他不纯的.NET组件进行互操作(但只有在您提供更具体的示例时才能回答)。
答案 1 :(得分:5)
您无需创建类来创建模拟:
/// customer : int -> string -> decimal -> ICustomer
let customer id name balance =
{new ICustomer with
member this.Id = id
member this.Name = name
member this.CalculateBalanceWithDiscount discount =
balance - (discount * balance) }
答案 2 :(得分:0)
type ICustomer = interface
abstract Id: int with get
abstract Name: string with get
abstract CalculateBalanceWithDiscount: decimal -> decimal
end
type Customer = class
val id: int
val name: string
val balance: decimal
new(id, name, balance) =
{id = id; name = name; balance = balance}
interface ICustomer with
member this.Id
with get () = this.id
member this.Name
with get () = this.name
member this.CalculateBalanceWithDiscount discount =
this.balance - (discount * this.balance)
end
end