Skip to content

Commit

Permalink
Allow properties in mocked interfaces (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
Smaug123 authored Feb 12, 2025
1 parent 129687e commit 277a186
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 2 deletions.
31 changes: 31 additions & 0 deletions ConsumePlugin/GeneratedMock.fs
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,34 @@ type internal TypeWithInterfaceMock =

interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
namespace SomeNamespace

open System
open WoofWare.Myriad.Plugins

/// Mock record type for an interface
type internal TypeWithPropertiesMock =
{
/// Implementation of IDisposable.Dispose
Dispose : unit -> unit
Prop1 : unit -> int
Prop2 : unit -> unit Async
Mem1 : string option -> string[] Async
}

/// An implementation where every method throws.
static member Empty : TypeWithPropertiesMock =
{
Dispose = (fun () -> ())
Prop1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop1"))
Prop2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop2"))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
}

interface TypeWithProperties with
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
member this.Prop1 = this.Prop1 ()
member this.Prop2 = this.Prop2 ()

interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
7 changes: 7 additions & 0 deletions ConsumePlugin/MockExample.fs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ type TypeWithInterface =
inherit IDisposable
abstract Mem1 : string option -> string[] Async
abstract Mem2 : unit -> string[] Async

[<GenerateMock>]
type TypeWithProperties =
inherit IDisposable
abstract Mem1 : string option -> string[] Async
abstract Prop1 : int
abstract Prop2 : unit Async
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,16 @@ module TestMockGenerator =
mock.Mem1 3 'a' |> shouldEqual "aaa"
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"

[<Test>]
let ``Example of use: properties`` () =
let mock : TypeWithProperties =
{ TypeWithPropertiesMock.Empty with
Mem1 = fun i -> async { return Option.toArray i }
Prop1 = fun () -> 44
}
:> _

mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]

mock.Prop1 |> shouldEqual 44
27 changes: 25 additions & 2 deletions WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ module internal InterfaceMockGenerator =
|> SynMemberDefn.memberImplementation
)

let properties =
interfaceType.Properties
|> List.map (fun pi ->
SynExpr.createLongIdent' [ Ident.create "this" ; pi.Identifier ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ Ident.create "this" ; pi.Identifier ] []
|> SynMemberDefn.memberImplementation
)

let interfaceName =
let baseName = SynType.createLongIdent interfaceType.Name

Expand All @@ -174,7 +183,7 @@ module internal InterfaceMockGenerator =

SynType.app' baseName generics

SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
SynMemberDefn.Interface (interfaceName, Some range0, Some (members @ properties), range0)

let access =
match interfaceType.Accessibility, spec.IsInternal with
Expand Down Expand Up @@ -248,14 +257,28 @@ module internal InterfaceMockGenerator =
|> SynField.make
|> SynField.withDocString (mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)

let constructProperty (prop : PropertyInfo) : SynField =
{
Attrs = []
Ident = Some prop.Identifier
Type = SynType.toFun [ SynType.unit ] prop.Type
}
|> SynField.make
|> SynField.withDocString (prop.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)

let createRecord
(namespaceId : LongIdent)
(opens : SynOpenDeclTarget list)
(interfaceType : SynTypeDefn, spec : GenerateMockOutputSpec)
: SynModuleOrNamespace
=
let interfaceType = AstHelper.parseInterface interfaceType
let fields = interfaceType.Members |> List.map constructMember

let fields =
interfaceType.Members
|> List.map constructMember
|> List.append (interfaceType.Properties |> List.map constructProperty)

let docString = PreXmlDoc.create "Mock record type for an interface"

let name =
Expand Down

0 comments on commit 277a186

Please sign in to comment.