如何在具有外部依赖项的f#中测试函数 [英] How to test functions in f# with external dependencies

查看:103
本文介绍了如何在具有外部依赖项的f#中测试函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难尝试对具有外部依赖项的F#代码进行单元测试.

I am having a hard time trying to unit test F# code with external dependencies.

在C#(我的背景)中,您通常会拥有一个传入了依赖项的类,然后可以重新使用它.抱歉,我的示例代码很愚蠢,但我只是想说明我的观点.

In C# (my background) you would typically have a class with a dependency passed in, which is then re-used. Apologies for my sample code, it's dumb but I'm just trying to illustrate my point.

public class Foo {

  IDependency d;
  public Foo(IDependency d) { this.d = d; }

  public int DoStuff(string bar) { return d.DoSomethingToStuff(bar); }

  public int DoMoreStuff(string bar) { 
     int i = d.DoSomethingToStuff(bar);
     return d.DoSomethingElseToStuff(bar, i);
  }
}

我正在尝试对F#求实,并避免使用类和接口(除非我需要与其他.NET语言互操作).

I'm trying to be pragmatic with F# and avoid using classes and interfaces (unless I need to interop with other .NET languages).

因此,在这种情况下,我的方法是让模块和某些函数的依赖项作为函数传递.我在此处

So my approach in this scenario is to have module and some functions with the dependencies passed in as functions. I found this tecnique here

module Foo

  let doStuff bar somethingFunc =
    somethingFunc bar

  let doMoreStuff bar somethingFunc somethingElseFunc =
    let i = somethingFunc bar
    somethingElseFunc bar i

此代码存在两个问题:

  1. 我需要不断传递我的依赖关系.在C#中,它在构造函数中传递并重新使用.您可以想象如果在多个地方使用somethingFunc会很快失去控制.

如何对已执行依赖项进行单元测试?再次在C#中,我将使用模拟框架并断言某些方法已被调用.

How do I unit test that dependencies have been executed? Again in C# I'd use a mocking framework and assert that certain methods were called.

在F#世界中如何解决这些问题?

How do I approach these problems in the F# world?

推荐答案

将诸如依赖项注入等SOLID概念映射到Functional-风格F#-关键之一就是要意识到对象与闭包之间的牢固关系.

It's not too difficult mapping SOLID concepts like Dependency Injection to Functional-style F# - one of the keys is to realize that there's a strong relationship between objects and closures.

在当前情况下,将有助于对函数参数进行重新排序,以使依赖项"首先出现:

In the present case, it would help to reorder the function arguments so that the 'dependencies' go first:

module Foo =    
  let doStuff somethingFunc bar =
    somethingFunc bar

  let doMoreStuff somethingFunc somethingElseFunc bar =
    let i = somethingFunc bar
    somethingElseFunc bar i

这将使您能够使用部分功能应用程序 组成功能:

This will enable you to compose functions using partial function application:

let doStuff' = Foo.doStuff somethingImp

现在,doStuff'是一个 closure ,因为它关闭了具体功能somethingImp.从本质上讲,它捕获了依赖关系,因此它的工作方式与具有注入的依赖关系的对象一样,您仍然可以使用其余的bar参数调用它:

Now, doStuff' is a closure, because it closes over the concrete function somethingImp. Essentially, it captures the dependency, so it works just like an object with an injected dependency, and you can still invoke it with the remaining bar argument:

let bar = 42
let actual = doStuff' bar


测试

以下是将局部函数用作存根的示例:

Here's an example of using local functions as stubs:

module Tests =

    let ``Data flows correctly through doMoreStuff`` () =
        let somethingFunc bar =
            assert (bar = 42)
            1337
        let somethingElseFunc bar i =
            assert (bar = 42)
            assert (i = 1337)
            "Success"

        let actual = Foo.doMoreStuff somethingFunc somethingElseFunc 42

        assert (actual = "Success")

在这里,为了简单起见,我使用了 assert关键字,但是为了进行适当的测试,您应该定义适当的断言函数,或使用自己喜欢的断言库.

Here, for the sake of simplicity, I've used the assert keyword, but for proper tests, you should define a proper assertion function, or use your favourite assertion library.

通常,我倾向于放宽对输入参数的验证,因为这可能会使测试双打与特定实现过于紧密地结合在一起.另外,请记住,您应该使用存根对于查询,对于命令来说是假的-在此示例中,只有查询,因此所有的测试双打都是存根:尽管它们确实验证了输入 if 的调用,但测试并未验证它们是否完全被调用.

Normally, I would tend to loosen the verification of input arguments, as it may make the Test Doubles too tightly coupled to a particular implementation. Also, keep in mind that you should use Stubs for Queries, and Mocks for Commands - in this example, there are only Queries, so all the Test Doubles are Stubs: although they do verify input if they are invoked, the test doesn't verify that they are invoked at all.

这篇关于如何在具有外部依赖项的f#中测试函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆