Mocking Non-Standard Events in F# Foq

2019-07-20 14:48发布

问题:

I'm new to F#, and I'm putting myself through some exercises to learn the language. The one I'm currently trying to do is to write a unit test for a custom Castle.Windsor Facility, and I am trying to Mock the Kernel to raise a "ComponentRegistered" event.

The tools I'm using are FsUnit/xUnit/Foq.

My Code:

let event = Event<_,_>()

let kernel = Mock<IKernel>()
    .SetupEvent(fun k -> <@ k.ComponentRegistered @>)
    .Publishes(event.Publish)
    .Create()

Error Message:

Error 4 The event 'ComponentRegistered' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_ComponentRegistered and remove_ComponentRegistered methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<>' or 'IEvent<,_>'. C:\Workbench\EvilDev\evildev.commons\Tests\EvilDev.Commons.Windsor.Tests\Auto Resolver Facility Specification.fs 35 53 EvilDev.Commons.Windsor.Tests

How do I mock/trigger this sort of event from F#?

回答1:

This type of event can be specified using a DelegateEvent:

let event = DelegateEvent<ComponentDataDelegate>()
let kernel = 
    Mock<IKernel>()
        .SetupEventByName("ComponentRegistered")            
        .Publishes(event.Publish)
        .Create()    
kernel.add_ComponentRegistered(fun _ _ -> 
    printf "Bingo"
)
event.Trigger([|"x";Mock.Of<IHandler>()|])

The add and remove handlers for the event can be discovered via reflection using the Type.GetEvent method.

I have extended the Foq API to allow events to be specified by name, so that you can specify the event name (untyped) to workaround the non-standard event type compiler error.

The change set to add the new SetupEventByName method has been committed. If you need this right now you can either build from source or simply add the latest Foq.fs to your project. This feature will be in Foq 1.5 which I will try and push in the next day or so.