Mocking of type/class with static method in fsharp

We are trying to mock type/class with static method , but unable to do it in fSharp, Is there any way to mock it? we are getting "* Type to mock ( TestRepository) must be an interface, a delegate, or a non-sealed, non-static class " error.

Sample Class :

type TestRepository<'V when 'V :> Iview> =
{ executeQuery: IQueryable<'T> ->Task<Result<seq<'T>, exn>> }
static member Create<'V when 'V :> Iview> state : TestRepository<'V> =
{
executeQuery = executeQuery<'V> typeof<'V>.FullName state
}

Welcome Aman!
How exactly are you trying to mock it? It sounds like you’re using some mocking library? I doubt that a mocking library is going to be able to mock a static member on a class. For that matter, it’s probably not going to be able to mock an F# record at all whether there is a static member or not because I believe F# records will be sealed, and the message says it only works for non-sealed classes. You could build a non-sealed class without using a record:

type TestRepository<'V when 'V :> Iview> (executeQuery : IQueryable<'T> -> Task<Result<seq<'T>, exn>>) =
  member this.ExecuteQuery src = executeQuery src
  static member Create<'V when 'V :> Iview> state = TestRepository (executeQuery<'V> typeof<'V>.FullName state)

though you still probably can’t mock the static member with your mocking library.

That said, if in your consumer you use the F# 7 SRTP syntax to refer to the static method:

let inline consumer<'a when 'a :> TestRepository<_> and 'a : (static member Create : State -> 'a) > =
  let repo = 'a.Create state
  ...

then you should be able to use any type that inherits from TestRepository and defines a static member Create, so you could probably roll your own mock rather than use a library.

SRTP is considered pretty advanced though, and I haven’t tested the code I wrote so YMMV. You might want to consider just simplifying the process by ditching mocks and using regular function passing and partial application:

let consumer (queryFactory : State -> (IQueryable<'T> -> Task<Result<seq<'T, exn>>)) = task {
  let execQuery = queryFactory state
  let! results = execQuery ...
}
...

// from prod - partially apply the real `executeQuery` as the factory function, just like your code sample
consumer (executeQuery<'V> typeof<'V>.FullName)
...

// from tests, "mock" a Repository, but without a mocking library
let mockQuery (_:IQueryable<'T>) = task { 
  printfn "Here I mock running a query"
  return Error (exn ("test failure"))
}

consumer (fun _ -> mockQuery)
1 Like