Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test functions in f# with external dependencies

Tags:

f#

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

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);
  }
}

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

The two problems I have with this code is:

  1. I need to keep passing my dependencies around. In C#, it's passed in the constructor and re-used. You can imagine how quickly this gets out of control if somethingFunc is used in several places.

  2. 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.

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

like image 368
Razor Avatar asked Mar 19 '23 19:03

Razor


1 Answers

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

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

Testing

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")

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.

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.

like image 84
Mark Seemann Avatar answered Apr 02 '23 04:04

Mark Seemann