Zaid-Ajaj / Fable.Mocha

Fable library for a proper testing story using different runners such as mocha, standalone browsers and dotnet
MIT License
56 stars 17 forks source link

run setup and teardown #11

Open forki opened 5 years ago

forki commented 5 years ago

Hi,

I have a promise setup and teardown function that I have to run. Any ideas how to do it? Of course I can't do runsync as in .NET code.

image

forki commented 5 years ago
    module End2End.Program

    open Fable.Mocha

    let allTests =
        testList "End2End tests" [
            SoundCheck.allTests
        ]

    promise {
        printfn "Init"
        do! TestHelper.init()

        Mocha.runTests allTests |> ignore

        printfn "Cleanup"
        do! TestHelper.cleanup()
        printfn "Done"
    }
    |> ignore

this seems to work. But is it the correct way?

forki commented 5 years ago

ok it's not working. it's not running my tests

Zaid-Ajaj commented 5 years ago

Hello @forki, I will take a look at it when I am back home after work. Meanwhile maybe you could try out the following:

[<Emit("before($0)")>]
let before (callback: unit -> Promise<unit>) = jsNative

[<Emit("after($0)")>]
let after(callback: unit -> Promise<unit>) = jsNative

let allTests = testList "End2End tests" [ SoundCheck.allTests ]

before (fun () -> TestHelper.init())
Mocha.runTests allTests |> ignore
after (fun () -> TestHelper.cleanup())

This should do the setup/tear down according to Mocha's API but you should be able to do it manually on a per-test basis by writing a helper function around testCasePromise that takes an IDisposable and does the setup/tear down internally.

In any case, please try it out and let me know how it goes!

forki commented 5 years ago

yes that works!

forki commented 5 years ago

in other words: would be cool to have the following in the box

[<Emit("before($0)")>]
let before (callback: unit -> Promise<unit>) = jsNative

[<Emit("after($0)")>]
let after(callback: unit -> Promise<unit>) = jsNative

[<Emit("beforeEach($0)")>]
let beforeEach (callback: unit -> Promise<unit>) = jsNative

[<Emit("afterEach($0)")>]
let afterEach (callback: unit -> Promise<unit>) = jsNative
MangelMaxime commented 5 years ago

Thoth.Fetch is using mocha for the tests. I don't use yet Fable.Moche so I have my own bindings in it. I am writing just this just as another reference.

Declaration

Usage

The difference I see is that I use unit -> unit instead of Promise<unit>, but it's not a big deal could use Promise<unit> if mocha support it too.

Zaid-Ajaj commented 5 years ago

@forki Yeah we could add those here but I am not sure if there is a better syntax to match Expecto's API and I don't know how far we want to stay compatible with it. @TheAngryByrd Do you have any ideas on the matter?

could use Promise if mocha support it too.

@MangelMaxime Mocha supports it indeed, but I guess if I implement it, I would implement both unit -> unit and unit -> Promise<unit> using function overloads

TheAngryByrd commented 5 years ago

I think as long as we don't touch the current API, any additions we make wouldn't affect it. (cc @AnthonyLloyd).

I've added my own helper functions to do similar things with setup/teardowns and IDisposables, so I think it's a good idea to add more if we can.

Maybe a slightly different but longer discussion is we need to break this into two libraries, Fable.Mocha which has exclusive wrappers for Mocha stuff and a Fable.Mocha.Expecto library which tries to keep an API compatible with Expecto.

TheAngryByrd commented 5 years ago

Thinking about it, IDisposable might not be a good idea here since Dispose is a synchronous call. I'm not sure if F#/Fable has a story around IAsyncDisposable yet.

MangelMaxime commented 3 years ago

I am writing test for a Database library and it would be really useful to have before, beforeEach, after, afterEach available in the library.

For example, it would make it possible to setup the database or clean it after running the tests. Right now, we can only have a before and after at the top level of the application as Fable.Mocha is using an internal "representation" to handle what needs to be done.

Is there plan for adding those functions? Should someone try to contribute it? If yes, what would be the way to go?

Zaid-Ajaj commented 3 years ago

@MangelMaxime PRs are always welcome. I think implementing something like Test fixtures from Expecto would be a good way to go, what do you think?

MangelMaxime commented 3 years ago

I suppose Test fixtures is something use to "mock" part of the application. Like if you want to keep the client connection or have a file based database like sqlite for exemple.

In my case, I would need the function I listed to prepared and clean the database for the tests. For example, it would allow me to create a schema, populate it with some data, etc. and then remove it from the database to maintain the database in a "neutral" state.

I don't think Expecto have an equivalent to what Fable.Mocha offer. The closer thing I see is: https://github.com/haf/expecto#setup-and-teardown where it is actually "just code" doing the job. I need to do more test locally to see if the Expecto approach can work and be an alternative to adding beforeEach, afterEach, etc. Because, I suppose Fable.Mocha doesn't aim to provide 100% of the Mocha API but probably more the sweet spot allowing the user to switch between Expecto and Fable.Mocha easily. I can be wrong ^^

MangelMaxime commented 3 years ago

Second though, because Fable.Mocha is a DSL on top of Mocha and does some code "transformation" I don't think I can have a a function called at the beginning and the end of a testList block.

Still need to test the beforeEach, afterEach case.

TheAngryByrd commented 3 years ago

At work I just implemented a function that allows for a setup/tear down to a testList for Expecto. I’d be willing to share that implantation (when I get to my computer) and see if aligning on the same signature is possible.

TheAngryByrd commented 3 years ago
    open System
    open Expecto
    open FSharp.Data.Adaptive

    let integrationTestFixtureList name oneTimeSetup tests =
      let myBase = cval Unchecked.defaultof<'a>
      let setupTest = testCaseAsync "Setup" <| async {
        let! result = oneTimeSetup
        myBase.UpdateTo result
      }
      let teardownTest = testCaseAsync "Teardown" <| async {
        // match box myBase with
        // | :? IDisposable as d -> d.Dispose()
        // | :? IAsyncDisposable as d -> do! d.DisposeAsync().AsTask() |> Async.AwaitTask
        // | _ -> ()
        // (myBase :> IDisposable).Dispose()
        do! (myBase.Value :> IAsyncDisposable).DisposeAsync().AsTask() |> Async.AwaitTask
      }
      let tests = [
        setupTest
        yield! tests |> Seq.map(fun t -> t myBase)
        teardownTest
      ]
      testSequencedGroup name (testListConditional (fun () -> runIntegrationTests) name tests)

I create a setup and teardown test set each. I created a specific setup test because I needed the setup to be lazy, otherwise it would try to run at testList enumeration which would cause it to run even if it tests were to be skipped. At work I ended up using FSharp.Data.Adaptive for this but we might be able to get away with a MailboxProcessor and inject a lazy Async<'MySetupType>. For the teardown I went with IAsyncDisposeable but I'm not completely tied to that implementation either. I have IDiseposeable for other ideas/usages.

Usage:

type DbConnections = {
  ConnStr : string
  Props : Sql.SqlProps
}

let setup = async {
  let connStr = getFromEnvVar("TEST_CONNECTION_STRING")
  let props =   Sql.connect connStr
  return {
    ConnStr = connStr
    Props = props
  }
}

let testList =
  integrationTestFixtureList "Some Database Stuff" setup [
    fun (conn : cval<AssetExpirationBase>) -> testCaseAsync "Test 1" <| async {
      let conn = conn.Value
      let expected = insertTestUser "13" conn.Props
      let actual = Repository.getUser conn.Props "13"
      Expect.equal actual expected ""
    }
  ]

I haven't made this suggestion to Expecto proper as of yet since we're still trying it at work and I'd like it to be robust before I do.