I'm having difficulties to Mock Microsoft ILogger with Foq.
module MockExample
open Microsoft.Extensions.Logging
open NUnit.Framework
open Foq
//open Foq.Linq
// Implementation
type Worker (logger:ILogger) =
member this.DoSomething (ok:bool) =
if ok then
logger.LogInformation("success 1")
else
logger.LogError("error 1")
// Test file
[<Test>]
let ``When error should log error`` () =
// 1. let logger = Mock<ILogger>().SetupAction(fun lg -> lg.LogError("error")) ...Create() not availabe
// 2.
//let loggerMock = Mock<ILogger>();
//loggerMock.SetupFunc(fun lg -> lg.LogError(It.IsAny<string>())) |> ignore // .Returns() is not usable
//let logger = loggerMock.Create()
let logger = mock()
// act
Worker(logger).DoSomething(false)
verify <# logger.LogError("error 1") #> once
//verify <# logger.LogError("success 1") #> never
Test does not pass.
I tried also other syntax to try to "mimic" the LogError signature but withut cuccess.
[EDIT]
As sugegsted I'm trying to mock the real logger method .Log(logLevel, message, ...), but still not able to get it working:
Essentially I don't know how to mock this:
member Log<'TState> : logLevel: LogLevel * eventId: EventId * state: 'TState * ``exception`` : exn * formatter: System.Func<'TState,exn,string> -> unit
to extract/register/compare the state (it contains the log "message").
[Edit 2]
As a workaround, I wrote my mock of the ILogger and it serve the purpose, but my question is still "how to do it with Foq" ?
type LoggerMock () =
let mutable informations:string list = []
let mutable errors:string list = []
member this.LoggedInformations = informations
member this.LoggedErrors = errors
interface ILogger with
member this.Log(logLevel: LogLevel, eventId: EventId, state: 'TState, ``exception``: exn, formatter: System.Func<'TState,exn,string>): unit =
match logLevel with
| LogLevel.Information -> informations <- informations # [state.ToString()]
| LogLevel.Error -> errors <- errors # [state.ToString()]
| _ -> ()
member this.BeginScope(state: 'TState): System.IDisposable =
raise (NotImplementedException())
member this.IsEnabled(logLevel: LogLevel): bool =
raise (NotImplementedException())
[<Test>]
let ``When error should log error (custom mock)`` () =
let logger = LoggerMock()
Worker(logger).DoSomething(false)
logger.LoggedErrors |> should contain "error 2"
The problem is that .LogError and .LogInformation are not actually methods on ILogger, but extension methods that wrap the log method that ILogger actually defines.
The method you need to mock is: ILogger.Log Method
I believe the extension methods all call that one method.
Related
With reference to Is there an equivalent of C#'s nameof(..) in F#?
how can the nameof function used or extended for the following case?
let nameof (q:Expr<_>) =
match q with
| Patterns.Let(_, _, DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _))) -> mi.Name
| Patterns.PropertyGet(_, mi, _) -> mi.Name
| DerivedPatterns.Lambdas(_, Patterns.Call(_, mi, _)) -> mi.Name
| _ -> failwith "Unexpected format"
let any<'R> : 'R = failwith "!"
let s = _nameof <# System.Char.IsControl #> //OK
type A<'a>() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof <# A<_>.MethodWith2Pars #> //Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
let s2 = nameof <# A<_>.MethodWith2Pars : Guid * string -> string #> //Same error
The compiler gives the following error:
Error FS0503 A member or object constructor 'MethodWith2Pars' taking 1 arguments is not accessible from this code location. All accessible versions of method 'MethodWith2Pars' take 2 arguments
The answer you linked is a bit outdated. F# 5.0 (released just recently) offers the true nameof feature. See the announcement: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
This feature also existed in preview since F# 4.7: https://devblogs.microsoft.com/dotnet/announcing-f-4-7/#nameof
You can write your code like this:
open System
type A() =
static member MethodWith2Pars(guid:Guid, str:string) = ""
static member MethodWith2Pars(guid:Guid, ba:byte[]) = ""
let s1 = nameof (A.MethodWith2Pars : Guid * byte[] -> string)
let s2 = nameof (A.MethodWith2Pars : Guid * string -> string)
The type annotation is needed due to overloading. Not sure why there is a generic type parameter on the class declaration, but it's not used anywhere so I just removed it.
Hi I have an extension method in f# that checks is TcpClinet is connected and it is rather simple code:
[<Extension>]
type TcpExtension() =
[<Extension>]
static member inline IsConnectionEstablished(client: TcpClient) : bool =
let ipProperties : IPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties()
let tcpConnection : Option<TcpConnectionInformation> = ipProperties.GetActiveTcpConnections() |> Seq.tryFind(fun connection -> connection.LocalEndPoint.Equals(client.Client.LocalEndPoint) && connection.RemoteEndPoint.Equals(client.Client.RemoteEndPoint))
match tcpConnection with
| Some connection -> true
| None -> false
Now I am trying to use it by simple:
let private WaitForData (client : TcpClient) =
let isConnectionAlive : bool = client.IsConnectionEstablished
isConnectionAlive
But I am getting message that looks like:
This function takes too many arguments, or is used in a context where a function is not expected
When I checked Microsoft documentation this is how they show it should be handled, what am I missing here?
The error message is a bit misleading, you're actually missing an argument
To invoke the function you need to pass in unit like if you were invoking it from C#
let isConnectionAlive : bool = client.IsConnectionEstablished()
I'm trying to write a generic json decode function in fable. It seems to compile in FSharp but I get the error message for this code:
[using the Thoth.Json library and the Fetch library from Fable.PowerPack]
let autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
error FABLE: Cannot get type info of generic parameter, please inline or inject a type resolver
I'm not sure how to fix this and haven't been able to find anything on google.
I want to be able to call the function like this in my update function in Fable Elmish:
let update (msg:Msg) (model:Model) =
match msg with
..
| OrStart ->
let getData() = Fetch.fetchAs<ResultResponse> "https://randomuser.me/api/" json.autoDecoder<ResultResponse> http.getHeaders
model, Cmd.ofPromise getData () LoadedTypedData FetchError
How can I get fable to compile the autoDecoder<'a> function while keeping it generic?
Thanks
I think Fable is telling you to use inline like this:
let inline autoDecoder<'a> (json:string) (value:obj) : Result<'a, Thoth.Json.Decode.DecoderError> =
let tryDecode = Thoth.Json.Decode.Auto.fromString<'a>(json)
let getDecoderError (str:string) : Thoth.Json.Decode.DecoderError = ( "Auto decode Error", Thoth.Json.Decode.FailMessage str)
Result.mapError getDecoderError tryDecode
That is because generic functions just like inline functions need to be instantiated for each call.
BTW, the value parameter is not being used.
You can also streamline the code like this:
let inline autoDecoder<'a> (json:string) : Result<'a, Thoth.Json.Decode.DecoderError> =
Thoth.Json.Decode.Auto.fromString<'a> json
|> Result.mapError (fun (str:string) -> "Auto decode Error", Thoth.Json.Decode.FailMessage str)
I'm new to fable and I wasn't able to get it working, the fable compiler does not allow the auto decode without a specified type - fails here:
Thoth.Json.Decode.Auto.fromString<'a>(str, true)
But for anyone struggling with the fetch api in fable, this can be done with not too much boilerplate code. I couldn't get the promise to be generic, but a type specific implementation like getCustomers is quite succinct and I ended up doing something like this:
type Msg =
| Start
| LoadedCustomerData of Result<QueryDataForJson, string>
..
let getCustomers () = promise {
let! response = Fetch.fetch "http://localhost:5000/spa/api/customers" http.getHeaders
let! text = response.text()
return Thoth.Json.Decode.Auto.fromString<QueryDataForJson>(text, true)
}
..
let update (msg:Msg) (model:Model) =
match msg with
| Start ->
model, Cmd.ofPromise getCustomers () LoadedCustomerData FetchError
| LoadedCustomerData resp ->
match resp with
| Ok qdj -> { model with gridData= queryDataFromJson qdj; message= "Loaded Customer Data" }, Cmd.none
| Error str -> { model with message = str }, Cmd.none
In F#, we can create interface instance by object expression, but while I'm trying to use attribute ReflectedDefinition on the instance method, then I cannot get the quotations. The method info is declared in the interface type, not the instance type.
Here is my test code:
module Test
open System
open System.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape
type IMyInterface =
abstract Foo : int -> int
let createMyInterface () =
{ new IMyInterface with
[<ReflectedDefinition>]
member this.Foo a = a + 1 }
let expr =
let a = createMyInterface()
<# a.Foo(42) #>
let rec iterExpr (expr:Expr) =
match expr with
| Call(objectExpr, info, paramExprs) ->
printfn "info: %A" info
printfn "reflected type: %A" info.ReflectedType
match info with
| MethodWithReflectedDefinition methodExpr ->
printfn "%A" methodExpr
| _ -> failwith "No reflected definition"
| ShapeVar _ -> failwithf "TODO: %A" expr
| ShapeLambda _ -> failwithf "TODO: %A" expr
| ShapeCombination _ -> failwithf "TODO: %A" expr
let test() =
iterExpr expr
[<EntryPoint>]
let main argv =
test()
0 // return an integer exit code
If I run it, I got exception:
C:\Users\Xiang\Documents\Inbox\TTTT\bin\Debug>TTTT
info: Int32 Foo(Int32)
reflected type: Test+IMyInterface
Unhandled Exception: System.Exception: No reflected definition
at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
at Test.iterExpr(FSharpExpr expr) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 30
at Test.test() in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 37
at Test.main(String[] argv) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 41
And I also checked the generated assembly with dotPeek, it is implemented as a derived class:
[CompilationMapping(SourceConstructFlags.ObjectType)]
[Serializable]
public interface IMyInterface
{
int Foo([In] int obj0);
}
[CompilationMapping(SourceConstructFlags.Closure)]
[Serializable]
[SpecialName]
[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
internal sealed class createMyInterface\u004014 : Test.IMyInterface
{
public createMyInterface\u004014()
{
base.\u002Ector();
Test.createMyInterface\u004014 createMyInterface14 = this;
}
[ReflectedDefinition]
int Test.IMyInterface.Test\u002DIMyInterface\u002DFoo([In] int obj0)
{
return obj0 + 1;
}
}
So, the problem is, when I call the Foo method in quotation, the Call pattern get MethodInfo which is declared at interface type, which has no definition. So how could I get the actually implementation MethodInfo? and then I can get the quotation of the implementation?
Here's your problem in a nutshell:
You're calling a virtual method through an instance of the type where the method is defined.
You want the quotation to contain a call to the method as defined on the derived class.
This won't work, and isn't limited to interfaces or object expressions:
type A() =
abstract M : unit -> unit
default this.M() = printfn "abstract"
type T() =
inherit A() with
[<ReflectedDefinition>]
override this.M() = printfn "override"
let expr =
let a : A = upcast T()
<# a.M() #>
Fundamentally, the whole point of an object expression is to provide an anonymous implementation of a non-sealed class, so what you're asking for doesn't make sense to me - the compiler only knows that the object is some instance implementing that interface but can't know the concrete type of the instance and therefore can't know which (of potentially many) concrete method to use.
I'd let to mock an IBus with Foq.
One of the methods on the IBus is OpenPublishChannel, which returns an IPublishChannel. IPublishChannel in turn has a Bus property that returns the parent IBus.
My current code is below, but obviously it doesn't compile as mockBus is not defined by the point I need it. Is there a way of setting up recursive mocking like this without creating two mocks of either interface?
open System
open EasyNetQ
open Foq
let mockChannel =
Mock<IPublishChannel>()
.Setup(fun x -> <# x.Bus #>).Returns(mockBus)
.Create()
let mockBus =
Mock<IBus>()
.Setup(fun x -> <# x.OpenPublishChannel() #>).Returns(mockChannel)
.Create()
Foq supports a Returns : unit -> 'TValue method so you can lazily create a value.
Using a little mutation instances can refer to each other:
type IPublishChannel =
abstract Bus : IBus
and IBus =
abstract OpenPublishChannel : unit -> IPublishChannel
let mutable mockBus : IBus option = None
let mutable mockChannel : IPublishChannel option = None
mockChannel <-
Mock<IPublishChannel>()
.Setup(fun x -> <# x.Bus #>).Returns(fun () -> mockBus.Value)
.Create()
|> Some
mockBus <-
Mock<IBus>()
.Setup(fun x -> <# x.OpenPublishChannel() #>).Returns(fun () -> mockChannel.Value)
.Create()
|> Some