5

I am using F# type definitions to prevent hard dependencies between my functions, for example

type IType1 = int -> int
type IType2 = int-> string

let func1 (i : int) : int = i * i
let func2 (i : int) : string = i |> string

let higherFunc (dep1 : IType1) (dep2 : IType2) (input : int) : string =
    input |> dep1 |> dep2

let curriedFunc = higherFunc func1 func2
let x = curriedFunc 2

output x : "4"

Obviously this is quite contrived and simple but imagine the dependencies are a parser and a sorter or whatever. Smaller grains of functionality that I am composing.

I am trying to use Foq to help with my unit test fixtures. This is my first week using F# properly and I am having a hard time trying to work out how to configure a mock of these types.

Two things are worth mentioning:

1 - I can make it work if I use abstract classes, but I don't want to do this as it's so much more hassle for exactly the same end result. For example

type IType1 = 
    abstract member doSomething : int -> int

type func1 () =
    interface IType1 with
        member this.doSomething (i: int) = i * i

allows me to set up a mock like

let mT1= Mock.With (fun (x : IType1) -> <@ x.doSomething(any()) --> 5 @>)

but I really don't want to have to do this.

2 - If I just use

type IType1 = int -> int
let mT1 = Mock.Of<IType1>()

then I get back a valid value, but if I try to configure it in any way like

let mT1= Mock<IType1>.With (fun x -> <@ x(any()) --> 5 @>)

or

let mT1= Mock<IType1>.With (fun x -> <@ any() --> 5@>)

then I get an exception of

System.NotSupportedException : Expected standard function application: Call 

or

System.NotSupportedException : Expected standard function application: ValueWithName 

I am hoping that I'm just being stupid with the syntax and that it is possible to do what I want. I have tried every variation I can think of, including variations of .Setup(conditions).Create(), and I can't find any examples in the source.

I can obviously easily make my own mocks like

let mT1 (i : int) : int = 5

as anything which fits that int -> int signature will be valid, but then if I want to check that the function was passed a certain value for i I have to put in a logging step etc etc.. It would just be nice to have Foq to do some of the heavy lifting.

Edit I just noticed that the root Mock object has 'requires reference type' in its signature ( i.e. Mock<'TAbstract(requires reference type)> ) - does that mean I have no chance of mocking values? How come it manages it if I don't configure the mock?

Ryan
  • 2,109
  • 1
  • 15
  • 19

1 Answers1

4

You don't have to mock. If your dependencies are just function types, you can just provide functions:

let mT1= fun x -> 5

The whole concept of object mocking was (had to be) invented by the object-oriented people to compensate for the fact that objects don't compose well (or at all). When your whole system is functional, you can just create functions on the spot. No mocking necessary.

If you're really hung up on using Foq's facilities like logging and verifying (which I urge you to reconsider: your testing would come out easier and more resilient), you can always make yourself an object that would act as a surrogate host for your functions:

type ISurrogate<'t, 'r> =
    abstract member f: 't -> 'r

// Setup
let mT1 = Mock.Create<ISurrogate<int, int>>()
mT1.Setup(...)...

let mT2 = Mock.Create<ISurrogate<int, string>>()
mT2.Setup...

higherFunc mT1.f mT2.f 42

mT1.Received(1).Call( ... ) // Verification

This way, the ugliness is confined to your tests and does not complicate your production code.

Obviously, this will only work for single-argument functions. For functions with multiple curried arguments, you'll have to tuple the arguments and wrap the call in a lambda at injection site:

// Setup
let mT1 = Mock.Create<ISurrogate<int * int, int>>()

higherFunc (fun x y -> mT1.f(x, y)) ...

If you find this happening often, you can pack the lambda creation for reuse:

let inject (s: ISurrogate<_,_>) x y = s.f (x,y)

higherFunc (inject mT1) ...
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • 1
    I actually addressed this in my post, with the last code example. I know I can do that, the point is if I want to check that my functions were passed the correct args, or called a certain number of times, I have to write that stuff. That's not the end of the world and it's what I am doing now, but it would be nice to use Foq if / when I wanted to. Simple fakes like the one we both mentioned are easy but a richer mock takes more effort. – Ryan Feb 17 '18 at 15:32
  • Yes, I was just adding that to the answer. Please check out the update. – Fyodor Soikin Feb 17 '18 at 15:42
  • r.e. the edit - again, I realised I could do this with abstract classes but I am trying to stick with function value types as the syntax is a lot cleaner and there are no objects involved. I know it's a good idea to test behaviour over implementation etc etc, but - for example I have a function which uses an api key from an inner function that builds the raw string and passes it through a hasher which was taken as a dependency. The only way to verify that the hasher was passed a valid string is either a.) mock it and log the input or b.) un-nest the api-key builder function to make it public – Ryan Feb 17 '18 at 15:46
  • I don't think you've read the edit quite well. I don't propose that you change your actual API to use objects. – Fyodor Soikin Feb 17 '18 at 15:48
  • Ok, yeah that's a potential solution, using abstract classes in my tests only, for interfaces which shadow the production code types. I think it's about as good an answer as I will get since I think configuring mocked function types directly isn't possible. Hopefully I can keep it to a minimum and just knock up my own funcs the majority of the time. Thanks for your help! – Ryan Feb 17 '18 at 15:57
  • The reason frameworks like Mock exists - at least in my experience - is not because 'the object people got it wrong', but because when you design by contract you want to verify that the contract is implemented correctly. This includes verifying how often a function is called and so on. I am not yet entirely convinced that functional languages do this better - if I have a set of functions searching or updating data in a database, perhaps one that currently has a SQL implementation, how can I tell someone who is going to implement a MongoDb implementation what I need? – Arwin Feb 23 '18 at 12:03
  • You can still write tests for functional code, you just don't have to use runtime code generation to do it. Try it out sometime, and make sure not to stick to object-oriented patterns, but really think about what the code is doing. It does indeed come out cleaner. – Fyodor Soikin Feb 23 '18 at 13:46
  • 1
    It did turn out that careful refactoring removed almost all of the situations where I needed to create a Foq mock rather than just write a quick func to pass in to my test.The one time I did use it was to check that a hashing func had been called with the correct args, as it saved me writing a log step, but it was trivial really. Ultimately as I moved away from larger aggregated funcs which my OO brain had come up with towards smaller, more focussed ones, testing became much simpler as Fyodor had suggested. – Ryan Feb 23 '18 at 18:14
  • I would also recommend FSUnit as a much nicer functional api for nUnit et al. @Arwin – Ryan Feb 23 '18 at 18:16
  • 1
    @Arwin - you can still design by contract. It's even easier than in OO as types are so easy to work with. I found and modified a very simple static IOC container to register my func values (not objects) against their type - here's a gist https://gist.github.com/RyanBuzzInteractive/0df64bdd5bce1eab53588a6314495ecb – Ryan Feb 23 '18 at 18:47
  • That is quite interesting, will definitely play with that some. But how would you solve something like this? Say I have some business logic that has a few state related functions, e.g. createContact, deleteContact, findContactById, findContactByName. My business logic should get these functions injected. Now I want an implementation of that in SQL that I can easily replace with an implementation of MongoDb, and when I write it the code should somehow enforce me to implement the contract correctly and warn me if something changed in a newer version, with tests that the contract was implemented. – Arwin Feb 26 '18 at 11:02
  • This is basically the problem that I do not yet fully know how to solve in F# elegantly. I can use functions as parameters just fine of course and this I use with tests, and then write my own fake functions with validations that they were called. But when a module requires a set of functions, it becomes more difficult. Each individual function does of course define what it needs, but the implementation of the full contract is not validated before the code is actually using it. Only each function individually. – Arwin Feb 26 '18 at 11:02
  • I am really new to F#, so please take all this with a large pinch of salt. When you say you have a module that requires functions, it seems you are thinking of it as a class, rather than just a convenient folder to store funcs in. Ideally, all of your funcs should get all of their dependencies passed in, not directly reference other funcs, even in the same module (although I have broken that for tiny helpers which I cba to abstract). You would never need all the CRUD operations in one function. You might make a flow to create a customer, which would depend on the ICreateCustomer type. – Ryan Feb 27 '18 at 14:13
  • This can be partially applied in the application setup, passing the concrete function you wish to use (see the usage example in that gist I posted). Just be careful not to inject impure functions into pure ones, as discussed in the Mark Seeman article I link to in the gist. – Ryan Feb 27 '18 at 14:13
  • As F# is strict about references, you can put all the type sigs in separate files to the implementations. These can be loaded last in the fsproj as they have no dependencies. The app setup is loaded first to do the wiring, as it depends on everything. This means that the rest of your project doesn't need to worry about load order. – Ryan Feb 27 '18 at 14:16
  • That's prob all a bit confusing, I tried to show what I mean here: https://gist.github.com/RyanBuzzInteractive/4092d784b46b4ec47a89c588a2525869 I've created an object wrapper around Sqlite to allow it to be interfaced. – Ryan Feb 27 '18 at 16:45
  • I don't think this answer provides the information @Ryan was looking for. If I got it right, he would like to generate a test double function, and later assert if that was called, and with which parameters, without the need to write a custom hosting abstract type (class / interface). I might be wrong, but the question looks to me still unanswered. I'm myself struggling to get to the same result Ryan described. – Arialdo Martini Feb 14 '22 at 17:29