F#: Unit Testing ViewModel Initialization

Advertisements

F#: ViewModel (Dependency Injection)

 

Intro

In this post, I attempt to demonstrate how cool I am at writing proven code for a user’s registration form. Please note that I use the term proven and testable interchangeably.

 

The viewmodel is the root of the testable codebase for business logic.
As a result, I injected the function (aka: implementation) that handles the submit request for a registration form inside my viewmodel.

Here’s code that shows a workflow accepting the implementation of a submit request inside my viewmodel:

type ViewModel(implementation:Try.SubmitFn) as x =
...
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function
                         | Some form ->
                                form |> Command.Execute
                                     |> In.SubmitRegistration.workflow implementation
                                     |> publishEvents
                         | None -> ()

Here’s the entire viewmodel:

namespace Nikeza.Mobile.UILogic.Registration

open System.Windows.Input
open Nikeza.Mobile.UILogic
open Nikeza.Mobile.Profile.Commands.Registration
open Nikeza.Mobile.Profile.Events
open Adapter
open In

type Form = Registration.Types.Form

module Updates =
    let statusOf formValidated events = 
        events |> List.exists formValidated

type ViewModel(implementation:Try.SubmitFn) as x =

    let mutable validatedForm = None

    let eventOccurred = new Event<_>()

    let validate() =
        let isValidated = function
            | FormValidated form -> validatedForm <- Some form; true
            | _ -> false

        { Form.Email=    x.Email
          Form.Password= x.Password
          Form.Confirm=  x.Confirm
        } 
          |> ofUnvalidated
          |> Validate.Execute 
          |> In.ValidateRegistration.workflow
          |> Updates.statusOf isValidated
               
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function 
                         | Some form -> 
                                form |> Command.Execute 
                                     |> In.SubmitRegistration.workflow implementation
                                     |> publishEvents
                         | None -> ()

    let validateCommand = DelegateCommand( (fun _ -> x.IsValidated <- validate()) , fun _ -> true)

    let submitCommand =   DelegateCommand( (fun _ -> submit() |> ignore), 
                                            fun _ -> x.IsValidated <- validate(); x.IsValidated )

    let mutable email =    ""
    let mutable password = ""
    let mutable confirm =  ""
    let mutable isValidated = false

    member x.Validate = validateCommand :> ICommand
    member x.Submit =   submitCommand   :> ICommand

    [<CLIEvent>]
    member x.EventOccured = eventOccurred.Publish

    member x.Email
             with get() =      email 
             and  set(value) = email <- value

    member x.Password
             with get() =      password
             and  set(value) = password <- value

    member x.Confirm
        with get() =      confirm
        and  set(value) = confirm <- value

    member x.IsValidated
             with get() =      isValidated
             and  set(value) = isValidated <- value

Test

An automated test targeting business logic should provide its own implementation for submitting a registration request.

My test for submitting a registration form is as follows:

[<Test>]
let ``Registration submitted after being validated`` () =
    
    // Setup
    let mutable registrationSucceeded = false

    let registration = ViewModel(mockSubmit)

    registration.FillIn()
                .EventOccured
                .Add(fun event -> 
                         event |> function
                                  | RegistrationSucceeded _ -> registrationSucceeded <- true
                                  | RegistrationFailed    _ -> registrationSucceeded <- false)

    registration.Validate.Execute()

    // Test
    registration.Submit.Execute()

    // Verify
    registrationSucceeded |> should equal true

Test API

As I stated earlier, I try to be cool when I write code. Thus, to validate my coolness, I used a type extension to initialize the registration viewmodel.

Here’s my test API:

module TestAPI

open Nikeza.Mobile.UILogic.Registration
open Try

let someEmail =    "scott@abc.com"
let somePassword = "some_password"

let mockSubmit : SubmitFn =
    fun _ -> Ok { Id =        ""
                  FirstName = ""
                  LastName =  ""
                  Email =     ""
                  Bio =       ""
                  ImageUrl =  ""
                  Sources =   []
                }

type ViewModel with

    member x.FillIn () =
           x.Email    <- someEmail
           x.Password <- somePassword
           x.Confirm  <- somePassword
           x

Usage

The workflow below relies on a SubmitFn which was originally injected into my viewmodel.

The workflow that receives the implementation of this dependency is as follows:

module SubmitRegistration =
    type private SubmitWorkflow = SubmitFn -> Command -> RegistrationSubmissionEvent list

    let workflow : SubmitWorkflow =
        fun submitFn command -> command |> function
            Command.Execute form ->
                            form |> submitFn
                                 |> ResultOf.Submit.Executed
                                 |> Are.Registration.Submission.events

Wiring a ViewModel to a DDD-Workflow

Intro

I recently connected a viewmodel to a workflow that I was inspired to write as a result of reading the book: Domain Driven Design Made Functional. All of my source code can be found on GitHub/Nikeza.

ViewModel

In the viewmodel, I handle validating and submitting a form. The interesting part of this exercise is that I receive domain events as a response to requesting validation and submission.

Here’s the logic for validating and submitting a form:

type ViewModel() as x =

    let mutable validatedForm = None

    let eventOccurred = new Event<_>()

    let validate() =
        let isValidated = function
            | FormValidated form -> validatedForm <- Some form; true
            | _ -> false

        { Form.Email=    x.Email
          Form.Password= x.Password
          Form.Confirm=  x.Confirm
        } 
        |> ofUnvalidated
        |> Validate.Execute 
        |> In.ValidateRegistration.workflow
        |> Updates.statusOf isValidated
               
    let submit() =
        let publishEvents events =
            events |> List.iter(fun event -> eventOccurred.Trigger(event))

        validatedForm |> function 
                         | Some form -> 
                                form |> Submit.Execute 
                                     |> In.SubmitRegistration.workflow
                                     |> publishEvents
                         | None -> ()

    let validateCommand = DelegateCommand( (fun _ -> x.IsValidated <- validate()) , fun _ -> true)

    let submitCommand =   DelegateCommand( (fun _ -> submit() |> ignore), 
                                            fun _ -> x.IsValidated <- validate(); x.IsValidated )

    let mutable email =    ""
    let mutable password = ""
    let mutable confirm =  ""
    let mutable isValidated = false

    member x.Validate = validateCommand :> ICommand
    member x.Submit =   submitCommand   :> ICommand

    [<CLIEvent>]
    member x.EventOccured = eventOccurred.Publish

The remaining viewmodel is the following:

type ViewModel() as x =

    ...
    member x.Email
             with get() =      email
             and  set(value) = email <- value

    member x.Password
             with get() =      password
             and  set(value) = password <- value

    member x.Confirm
        with get() =      confirm
        and  set(value) = confirm <- value

    member x.IsValidated
             with get() =      isValidated
             and  set(value) = isValidated <- value

Here’s some viewmodel helpers:

type UIForm = Registration.Types.Form
type DomainForm = Nikeza.Mobile.Profile.Registration.Form

module That =
    let generatesPage events =
        let eventToPage = function
        | RegistrationSucceeded _ -> () // displayPortal()
        | RegistrationFailed    _ -> () // displayError()

        events |> List.iter eventToPage

module Updates =
    let statusOf formValidated events =
        events |> List.exists formValidated

Registration Workflow

Here’s the Registration workflow:

module In

open Nikeza.Mobile.Profile.Commands
open Nikeza.Mobile.Profile.Commands.Registration
open Nikeza.Mobile.Profile.Events
open Logic

module SubmitRegistration =
    type private SubmitWorkflow = Submit -> RegistrationSubmissionEvent list

    let workflow : SubmitWorkflow =
        fun command -> command |> function
            Submit.Execute form ->
                form |> Try.submit
                     |> ResultOf.Submit.Executed
                     |> Are.Registration.Submission.events

module ValidateRegistration =
    type private ValidateWorkflow = Validate -> RegistrationValidationEvent list

    let workflow : ValidateWorkflow =
        fun command -> command |> function
            Validate.Execute form ->
                             form |> Registration.validate
                                  |> ResultOf.Validation.Executed
                                  |> Are.Registration.Validation.events

Domain Events

The domain events are structured as follows:

module Nikeza.Mobile.Profile.Events

open Nikeza.Common
open Nikeza.DataTransfer

type UnvalidatedForm = Nikeza.Mobile.Profile.Registration.UnvalidatedForm
type ValidatedForm =   Nikeza.Mobile.Profile.Registration.ValidatedForm

type RegistrationValidationEvent =
    | FormValidated         of ValidatedForm
    | FormNotValidated      of UnvalidatedForm

type RegistrationSubmissionEvent =
    | RegistrationSucceeded of Profile
    | RegistrationFailed    of ValidatedForm

Here’s how these events get emitted:

module internal Are.Registration

open Nikeza.Mobile.Profile.Commands
open Nikeza.Mobile.Profile.Events

module Submission =
    type private RegistrationSubmission = ResultOf.Submit -> RegistrationSubmissionEvent list

    let events : RegistrationSubmission =
        fun resultOf -> 
            resultOf |> function
                        ResultOf.Submit.Executed result -> 
                                                 result |> function
                                                         | Ok    profile -> [ RegistrationSucceeded profile]
                                                         | Error form    -> [ RegistrationFailed    form ]

module Validation =
    type private RegistrationValidation = ResultOf.Validation -> RegistrationValidationEvent list

    let events : RegistrationValidation =
        fun resultOf -> 
            resultOf |> function
                        ResultOf.Validation.Executed result -> 
                                                     result |> function
                                                               | Error form -> [FormNotValidated form]
                                                               | Ok    form -> [FormValidated    form]

Tests

Here’s the test that I wrote:

module Tests.Registration

open FsUnit
open NUnit.Framework
open Nikeza.Mobile.UILogic.Registration
open TestAPI
open Nikeza.Mobile.Profile.Events

[<Test>]
let ``Registration validated with email and matching passwords`` () =
    
    // Setup
    let registration = ViewModel()
    registration.Email    <- someEmail
    registration.Password <- somePassword
    registration.Confirm  <- somePassword

    // Test
    registration.Validate.Execute()

    // Verify
    registration.IsValidated |> should equal true

[<Test>]
let ``Registration submitted after being validated`` () =
    
    // Setup
    let mutable registrationSucceeded = false

    let registration = ViewModel()
    Apply.valuesTo registration

    registration.EventOccured.Add(fun event -> 
                                      event |> function
                                               | RegistrationSucceeded _ -> registrationSucceeded <- true
                                               | RegistrationFailed    _ -> registrationSucceeded <- false)
    registration.Validate.Execute()

    // Test
    registration.Submit.Execute()

    // Verify
    registrationSucceeded |> should equal true

 

F#: Domain Modeling Workflows (vol. 5)

Portfolio.Workflow

module Workflows.Commands

open Commands
open Events

type private LinksWorkflow =  LinkCommand   -> LinksEvent  list
type private TopicsWorkflow = TopicsCommand -> TopicsEvent list

let handleLinks : LinksWorkflow = fun command -> command |> function
    | LinkCommand.Feature   linkId -> 
                            linkId |> Try.featureLink
                                   |> ResultOf.Link.Feature
                                   |> Handle.Registration.result
                                   
    | LinkCommand.Unfeature linkId -> 
                            linkId |> Try.unfeatureLink
                                   |> ResultOf.Link.Feature
                                   |> Handle.Registration.result

let handleTopics : TopicsWorkflow = fun command -> command |> function
    TopicsCommand.Feature topicIds ->
                          topicIds |> Try.featureTopics
                                   |> ResultOf.Topics.Feature
                                   |> Handle.Topics.result

Portfolio.Logic

module internal Handle

open Events
open Commands
open Commands.ResultOf

module Registration =

    type private LinkResult =   ResultOf.Link   -> LinksEvent list

    let linkResult : LinkResult =
        fun response ->
            response |> function
                        | Link.Feature   result -> 
                                         result |> function
                                                   | Ok    linkId -> [LinkFeatured       linkId]
                                                   | Error linkId -> [LinkFeaturedFailed linkId]
                        | Link.Unfeature result -> 
                                         result |> function
                                                   | Ok    linkId -> [LinkUnfeatured       linkId]
                                                   | Error linkId -> [LinkUnfeaturedFailed linkId]

module Topics =
    type private TopicsResult = ResultOf.Topics -> TopicsEvent list

    let result : TopicsResult =
        fun response ->
            response |> function
                        Topics.Feature result -> 
                                       result |> function
                                                 | Ok    topicIds -> [TopicsFeatured       topicIds]
                                                 | Error topicIds -> [TopicsFeaturedFailed topicIds]

F#: Domain Modeling a Workflow (vol. 5)

Intro

I’m practicing Domain Modeling Made Functional and have implemented the Profile domain context of Nikeza.Mobile.

Here’s the birds-eye view of the workflows I constructed:

Untitled2

Workflow

The workflows that I wrote reflect operations that rely on I/O on the edges and domain logic at the core. All workflows take a command and emit domain events as defined upfront via function type declarations. These function types are RegistrationWorkflow, SessionWorkflow, and EditWorkflow.

module Workflows

open Commands
open Events
open Logic

type private RegistrationWorkflow = RegistrationCommand -> RegistrationEvent list
type private SessionWorkflow =      SessionCommand      -> SessionEvent      list
type private EditWorkflow =         EditCommand         -> ProfileEvent      list

let handleRegistration : RegistrationWorkflow =
    fun command -> command |> function
    | RegistrationCommand.Validate form ->
                                   form |> Registration.validate
                                        |> ResultOf.Registration.Validate
                                        |> Registration.handle
    | RegistrationCommand.Submit   form ->
                                   form |> IO.trySubmit
                                        |> ResultOf.Registration.Submit
                                        |> Registration.handle
let handleSession : SessionWorkflow =
    fun command -> command |> function
    | SessionCommand.Login credentials ->
                           credentials |> IO.tryLogin
                                       |> ResultOf.Login
                                       |> Session.handle

    | SessionCommand.Logout -> IO.tryLogout()
                                 |> ResultOf.Logout
                                 |> Session.handle
let handleEdit : EditWorkflow =
    fun command -> command |> function
    | EditCommand.Validate profile ->
                           profile |> Edit.validate
                                   |> ResultOf.Editor.Validate
                                   |> Edit.handle
    | EditCommand.Save     profile ->
                           profile |> IO.trySave
                                   |> ResultOf.Editor.Save
                                   |> Edit.handle

Commands

Commands are partitioned via responsibilities. Hence, I have command types for supporting registration, user-session, and editing.

The result of command execution is reflected by the ResultOf module that’s defined under the command types. I used similar naming conventions to enhance readability specifically for the Workflows module.

module Commands

open Nikeza.DataTransfer
open Registration

type RegistrationCommand =
    | Validate of Registration.UnvalidatedForm
    | Submit   of Registration.ValidatedForm

type SessionCommand =
    | Login  of Credentials
    | Logout 

type EditCommand =
    | Validate of EditedProfile
    | Save     of ValidatedProfile

    module ResultOf =
        type Editor =
            | Validate of Result<ValidatedProfile, EditedProfile>
            | Save     of Result<Profile, ValidatedProfile>

        type Session =
            | Login  of Result<Provider, Credentials>
            | Logout of Result<unit, unit>

        type Registration =
            | Submit   of Result<Nikeza.DataTransfer.Profile, Registration.ValidatedForm>
            | Validate of Result<ValidatedForm, UnvalidatedForm>

Events

Domain events are emitted from a workflow. These events are organized under different domain responsibilities (i.e. Registration, Session, Edit).

module Events

open Nikeza.Common
open Nikeza.DataTransfer

type RegistrationEvent =
    | FormValidated         of Registration.ValidatedForm
    | FormNotValidated      of Registration.UnvalidatedForm
    | RegistrationSucceeded of Profile
    | RegistrationFailed    of Registration.ValidatedForm
    | LoginRequested        of ProfileId

type SessionEvent =
    | LoggedIn    of Provider
    | LoginFailed of Credentials
    | LoggedOut
    | LogoutFailed

type ProfileEvent =
    | ProfileValidated    of ValidatedProfile
    | ProfileNotValidated of EditedProfile
    | ProfileRequested    of ProfileId
    | ProfileSaved        of Nikeza.DataTransfer.Profile
    | ProfileSaveFailed   of ValidatedProfile
    | Subscribed          of ProviderId
    | Unsubscribed        of ProviderId

I/O

The IO module is responsible for executing impure functions that have a dependency on the outside world. Thus, I use a Result type to reflect the status of the I/O operations.

module internal IO

open Registration
open Nikeza.DataTransfer

type private TrySubmit = ValidatedForm    -> Result<Profile, ValidatedForm>
type private TryLogin =  Credentials      -> Result<Provider, Credentials>
type private TryLogout = unit             -> Result<unit, unit>
type private TrySave =   ValidatedProfile -> Result<Profile, ValidatedProfile>

let trySubmit : TrySubmit =
    fun form -> Error form

let tryLogout : TryLogout =
    fun () -> Error ()

let tryLogin : TryLogin =
    fun credentials -> Error credentials

let trySave : TrySave =
    fun profile -> Error profile

Logic.editor.fs

Here’s the domain logic for editing a user’s profile:

module internal Logic.Edit

open Nikeza.DataTransfer
open Events
open Commands

type private Validate = EditedProfile   -> Result<ValidatedProfile, EditedProfile>
type private Handle =   ResultOf.Editor -> ProfileEvent list

let validate : Validate =
    fun edit ->
        let validEmail =     not <| System.String.IsNullOrEmpty(edit.Profile.Email)
        let validFirstName = not <| System.String.IsNullOrEmpty(edit.Profile.FirstName)
        let validLastName =  not <| System.String.IsNullOrEmpty(edit.Profile.LastName)         if validEmail && validFirstName && validLastName            then Ok    { Profile= edit.Profile }            else Error { Profile= edit.Profile } let handle : Handle =     fun response ->
        response |> function
                    | ResultOf.Editor.Validate result ->
                                               result |> function
                                                         | Ok    profile -> [ProfileValidated    profile]
                                                         | Error profile -> [ProfileNotValidated profile]
                    | ResultOf.Editor.Save     result ->
                                               result |> function
                                                         | Ok    profile -> [ProfileSaved      profile]
                                                         | Error profile -> [ProfileSaveFailed profile]

Logic.session.fs

Here’s the domain logic for managing a user’s session:

module internal Logic.Session

open Commands
open Events

type private HandleLogin = ResultOf.Session -> SessionEvent list

let handle : HandleLogin =
    fun result ->
        result |> function
                  | ResultOf.Session.Login result ->
                                           result |> function
                                                     | Ok    info -> [LoggedIn    info]
                                                     | Error info -> [LoginFailed info]
                  | ResultOf.Session.Logout result ->
                                            result |> function
                                                      | Ok    _ -> [LoggedOut]
                                                      | Error _ -> [LogoutFailed]

Logic.registration.fs

Here’s the domain logic for managing a user’s registration:

module internal Logic.Registration

open Nikeza.Common
open Events
open Registration
open Commands

type private Registration = ResultOf.Registration -> RegistrationEvent list

let handle : Registration =
    fun resultOf -> resultOf |> function
        | ResultOf.Registration.Submit   result ->
                                         result |> function
                                                   | Ok    profile -> [RegistrationSucceeded profile]
                                                   | Error form    -> [RegistrationFailed    form]
        | ResultOf.Registration.Validate result ->
                                         result |> function
                                                   | Ok    form -> [FormValidated    form]
                                                   | Error form -> [FormNotValidated form]

let validate (unvalidatedForm:UnvalidatedForm) : Result<ValidatedForm, UnvalidatedForm> =

    let isValidEmail email = false

    let form = unvalidatedForm.Form

    if   not (form.Email |> isValidEmail) then
          Error unvalidatedForm

    elif form.Password <> form.Confirm then
          Error unvalidatedForm

    else  Ok { Form= form }

let isValid (credentials:LogInRequest) =
    let validEmail =    not <| System.String.IsNullOrEmpty(credentials.Email)
    let validPassword = not <| System.String.IsNullOrEmpty(credentials.Password)

    validEmail && validPassword

F#: Domain Modeling a Workflow (vol. 4)

 

Preface

Let me first send a Shoutout to the F# Camp for helping me continue to grow as a professional developer down here in Miami, Florida. Their support has assisted my relentless pursuit to achieve mastery.

Specifically:
Scott Wlaschin
Mark Seemann
The InnerLight
Chad Gilbert

Intro

I’ve been going HAM in Functional Programming. The following brain dump reflects my current knowledge of Domain Driven Design made Functional.

Workflow

I define a workflow within Nikeza.Mobile as a series of steps to accomplish an objective.
Thus, the following are two workflows for a Follow and an Unsubscribe command:

module Workflows

open Commands
open IO
open Logic
open Events

type private Workflow = Command -> NotificationEvent list

let handle : Workflow =
    fun command -> command |> function
    | Command.Follow      request -> request
                                      |> tryFollow
                                      |> ResultOf.Follow
                                      |> handle

    | Command.Unsubscribe request -> request
                                      |> tryUnsubscribe
                                      |> ResultOf.Unsubscribe
                                      |> handle

Command

Commands are simply requests that can be executed.
In my case, I have a Follow command and an Unsubscribe command:

module Commands

open Nikeza.Common

type Command =
    | Follow      of FollowRequest
    | Unsubscribe of UnsubscribeRequest

I/O

Industry experts within the Software Engineering space recommend that impure functions (i.e. I/O) be done at the edges of a workflow. As a result, I have I/O operations at the front edge of my Logic code.

Here’s some IO requests:

module IO

open Nikeza.Common
open Nikeza.DataTransfer

type TryRequest<'request, 'response, 'errorInfo> =
                'request -> Result<'response, 'errorInfo>

let tryFollow :       TryRequest<FollowRequest, SubscriptionResponse, ProfileId> =
    fun (request:FollowRequest) ->
        Error (request.ProfileId |> ProfileId)

let tryUnsubscribe :  TryRequest<UnsubscribeRequest, SubscriptionResponse, ProfileId> =
    fun (request:UnsubscribeRequest) ->
        Error (request.ProfileId |> ProfileId)

type ResultOf =
    | Follow      of Result<SubscriptionResponse, ProfileId>
    | Unsubscribe of Result<SubscriptionResponse, ProfileId>

Logic

The result of executing pure domain logic should be domain events.
Here’s the pure domain logic that handles the result of an IO request:

module Logic

open IO
open Events

type private Logic = ResultOf -> NotificationEvent list

let handle : Logic =
    fun command -> command |> function
    | Follow      response -> response |> function
                                          | Ok    info       -> [SubscriberAdded        info.User]
                                          | Error profileId  -> [SubscriberAddFailed    profileId]

    | Unsubscribe response -> response |> function
                                          | Ok    info       -> [SubscriberRemoved      info.User]
                                          | Error profileId  -> [SubscriberRemoveFailed profileId]

Events

A workflow emits domain events. Hence, looking at the Workflow module, one would observe that the result of the handle function are emitted domain events.

Here are some events:

module Events

open Nikeza.DataTransfer
open Nikeza.Common

type NotificationEvent =
    | LinksDiscovered        of Provider

    | SubscriberAdded        of User
    | SubscriberAddFailed    of ProfileId

    | SubscriberRemoved      of User
    | SubscriberRemoveFailed of ProfileId