1

我正在尝试使用 Foq 来测试与 Foq 的接口。

到目前为止,我看到的所有例子都比较简单,例如:

let users = [|{ID = 1; pass = true};{ID = 2; pass= false}|]

type IFoo = 
    abstract member Bar: int -> bool

//tests with Foq
let dataAccess = 
    Mock<IFoo>()
    .Setup(fun x-> <@ x.Bar(users.[0].ID)  @>).Returns(users.[0].pass)
    .Setup(fun x-> <@ x.Bar(users.[1].ID)  @>).Returns(users.[1].pass)
    .Create()

示例来自“使用 F# 进行测试 - Mikael Lundin”

我还通过一些谷歌搜索对此进行了研究(此链接很有帮助 - http://trelford.com/blog/post/Foq.aspx

但是,我要测试的真实接口如下:

type IParameters =
    abstract member ParameterDate : int->string->DateTime 

type IDataSource =
    abstract member MortParameters: IParameters

我尝试了许多不同的方法来测试这些(例如,定义一个带有 int->string 签名的函数,用作设置的输入。或者,将返回值作为 string->DateTime 并将设置作为只是一个整数。

我的问题实际上如下:使用 Foq 测试接口时,如何将测试扩展到具有任何一般长度的函数签名的接口(例如 a->b->c->d->e 等)

4

1 回答 1

0

由于ParameterDate具有函数类型的属性,您可以将其设置为返回 lambda 值的属性。请参阅Foq 中的属性设置示例。这应该很容易根据您的情况进行修改:

let instance =
    Mock<System.Collections.IList>()
        .Setup(fun x -> <@ x.Count @>).Returns(1)
        .Create()

但是,我想您将失去对函数输入具有固定期望的严格模拟的能力。


要仅强制模拟属性返回的函数的预期输入,您可以提供如下函数:

fun i s ->
    match i, s with
    | 1, "" -> DateTime.Now
    | _ -> failwith "Invalid mock input"

我可能会在这里停下来,但是如果您正在使用需要验证函数被调用的代码,而不是仅仅确保您获得正确的输出,您可以添加一个这样的帮助程序:

type Verifiable<'a, 'b> (f : 'a -> 'b) =
    let called = ref false
    member this.Func x =
        called := true
        f x
    member this.Verify() =
        if not called.Value then failwith "Mock function was not called"

以下是您将如何使用它:

let parameterDateMock =
    fun i s ->
        match i, s with
        | 1, "" -> DateTime.Now
        | _ -> failwith "Unexpected mock input"
    |> Verifiable

let parameters =
    { new IParameters with member this.ParameterDate i s = parameterDateMock.Func i s }

parameters.ParameterDate 1 ""

parameterDateMock.Verify()

警告:这仅验证函数是否使用至少一个参数调用。它可能通过柯里化返回了另一个函数,而不是实际运行模拟函数体中的代码。为了解决这个问题,您需要为每个函数 arity 使用 Verifiable 类的变体,并在每种情况下使用正确的类。

于 2016-05-09T10:04:43.453 回答