4

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?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Razor
  • 17,271
  • 25
  • 91
  • 138
  • 2
    People probably won't like this comment, but: learn about side effects and see http://en.wikibooks.org/wiki/Haskell/Testing . "I'm trying to be pragmatic with F# and avoid using classes and interfaces" <- why? There's nothing pragmatic about this. – Mauricio Scheffer Aug 08 '14 at 06:08
  • 1
    Related: http://stackoverflow.com/a/22266609/126014 – Mark Seemann Aug 08 '14 at 06:29
  • 1
    @MauricioScheffer I have read several blog posts about treating F# as a functional language and using functions which operate on data structures rather than encapsulating data and behaviour. In any case, I don't want to treat F# like a classical OO language. – Razor Aug 08 '14 at 10:47
  • 1
    @VincePanuccio OO or not does not matter much... keep your code composable and as side-effect-free as you can. – Mauricio Scheffer Aug 08 '14 at 16:13
  • @VincePanuccio the thing is there is no such shoehorning. It's useful to separate these three concepts in one's mind: objects, imperative, side-effects. – Mauricio Scheffer Aug 09 '14 at 00:17

1 Answers1

7

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.

Community
  • 1
  • 1
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • 1
    How do I unit test to ensure a function was called? Although it's easy enough to see with this example, for more complicated logic I'd want to verify certain functions were called. – Razor Aug 08 '14 at 10:44
  • 2
    @VincePanuccio "How do I unit test to ensure a function was called?" <- don't. Your unit tests should be just passing some data to the function being tested and then checking the result. – Mauricio Scheffer Aug 08 '14 at 18:22
  • 3
    @MauricioScheffer That's the best approach, but that requires you to design without mutation, which I'm sure *you* know how to do :) Still, F# isn't a 'pure' Functional language, and sometimes, it can be helpful to verify that a Command was invoked - that's what I explain in my [Mocks for Commands, Stubs for Queries](http://blog.ploeh.dk/2013/10/23/mocks-for-commands-stubs-for-queries) article. TBC: in a pure Functional program, there are no Commands, and thus, no need for Mocks, so I'm not disagreeing. In an impure Functional program, you may have a few Commands, and thus, a few Mocks. – Mark Seemann Aug 09 '14 at 09:03