How to test functions in f # with external dependencies

I find it difficult using unit test F # code with external dependencies.

In C # (my background) you will usually have a class with a passed dependency, which is then reused. Sorry 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 interact with other .NET languages).

So my approach in this scenario is to have a module and some functions with dependencies being passed 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 

Two problems that I have with this code:

  • I need to continue to pass on my dependencies. In C #, it is passed in the constructor and reused. You can imagine how quickly this gets out of hand if somethingFunc used in several places.

  • How do unit tests run dependencies? Again in C # I will use a mocking structure and claim that certain methods have been called.

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

+5
source share
1 answer

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.

+8
source

All Articles