如何用f#用模拟编写测试

时间:2010-03-29 10:29:33

标签: unit-testing f# mocking

我想用模拟对象编写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

3 个答案:

答案 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
相关问题