Matching SOLID concepts such as Injection Dependency to Function F # is not too difficult - one of the keys is to realize that there is a strong relationship between objects and closures .
In this case, it will help to reorder the arguments of the function, so that the dependencies first:
module Foo = let doStuff somethingFunc bar = somethingFunc bar let doMoreStuff somethingFunc somethingElseFunc bar = let i = somethingFunc bar somethingElseFunc bar i
This will allow you to create functions using a partial function application :
let doStuff' = Foo.doStuff somethingImp
Now doStuff' is a closure because it closes over a specific somethingImp function. Essentially, it captures the dependency, so it works exactly like an object with an injected dependency, and you can still call it with the remaining argument bar :
let bar = 42 let actual = doStuff' bar
Testing
Here is 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 simplicity, I used the assert keyword , but for the right tests, you must determine the correct statement of the function or use your favorite statement library.
Usually I would like to weaken the verification of input arguments, as this may lead to the fact that double test pairs are too closely related to a specific implementation. Also, keep in mind that you should use Stubs for Queries and Mocks for Commands - in this example there are only queries, therefore all Test pairs are stubs: although they check input, if they are called, the test does not check that they are called at all.