Skip to content

Commit 40d1e43

Browse files
committed
feat: enhance test suite with async support and improved structure
1 parent 458b358 commit 40d1e43

File tree

6 files changed

+107
-114
lines changed

6 files changed

+107
-114
lines changed

Readme.md

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,31 +51,36 @@ open Sprout
5151
5252
let suite = describe "A test suite" {
5353
Info "Top level info message"
54+
55+
// variables may be used to store state across tests
56+
let mutable b = false
57+
beforeEach { b <- true }
5458
it "should pass" {
5559
info "This test passes"
60+
61+
// simple assertions included out-of-the-box
62+
b |> shouldBeTrue
5663
}
5764
5865
it "should fail" {
5966
info "This test fails"
6067
failwith "Intentional failure"
6168
}
6269
70+
// use pending to mark tests that are not yet implemented
6371
pending "This is a pending test"
6472
65-
describe "Nested suite" {
66-
Debug "Use beforeEach and afterEach for setup and teardown"
67-
beforeEach {
68-
debug "Before each test"
69-
}
73+
describe "Async works too" {
74+
Debug "Async test example"
7075
71-
afterEach {
72-
debug "After each test"
73-
}
74-
it "should also pass" {
75-
info "Nested test passes"
76+
// asynchronous flows are supported
77+
it "should run asynchronously" {
78+
do! Async.Sleep 1000
79+
info "Async test completed"
7680
}
7781
}
7882
83+
// use nested suites to organize tests
7984
describe "Arithmetic" {
8085
describe "Addition" {
8186
it "should add two numbers correctly" {
@@ -90,24 +95,30 @@ let suite = describe "A test suite" {
9095
result |> shouldEqual 9
9196
}
9297
}
93-
}
9498
95-
describe "Comparisons" {
96-
debug "Testing comparisons"
97-
it "should compare numbers correctly" {
98-
5 > 3 |> shouldBeTrue
99+
describe "Comparisons" {
100+
debug "Testing comparisons"
101+
it "should compare numbers correctly" {
102+
5 > 3 |> shouldBeTrue
103+
}
99104
}
100-
}
101105
102-
describe "Parameterized Tests" {
103-
info "Simply embed test cases and loop over them"
104-
let numbers = [1; 2; 3; 4; 5]
105-
for n in numbers do
106-
it $"should handle number {n}" {
107-
n > 0 |> shouldBeTrue
108-
}
106+
// parameterized tests are supported using regular F# loops
107+
// type-safe as expected without any special syntax
108+
describe "Parameterized Tests" {
109+
info "Simply embed test cases and loop over them"
110+
let numbers = [1; 2; 3; 4; 5]
111+
for n in numbers do
112+
it $"should handle number {n}" {
113+
n > 0 |> shouldBeTrue
114+
}
115+
}
109116
}
110117
}
118+
119+
// Run the test suite asynchronously
120+
runTestSuite
121+
|> Async.RunSynchronously
111122
```
112123

113124
Output:

Sprout.fs

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,30 @@ type ITestReporter =
7272
abstract Debug : message:string * path:Path -> unit
7373
abstract End : TestResult [] -> unit
7474

75-
type SimpleAsyncBuilder() =
75+
module Builders =
76+
type EachBuilder(factory: (unit -> Async<unit>) -> EachFunction) =
7677
member _.Zero() = async { return () }
78+
member _.Return x = async { return x }
79+
member _.ReturnFrom(x: Async<unit>) = x
7780
member _.Delay(f: unit -> Async<unit>) = async.Delay f
78-
member _.Run(f: Async<unit>) = f
79-
member _.Bind(m: Async<unit>, f: unit -> Async<unit>) = async.Bind(m, f)
80-
81-
let simpleAsync = SimpleAsyncBuilder()
82-
83-
let computation =
84-
simpleAsync {
85-
do! async { printfn "First" }
86-
do! async { printfn "Second" }
87-
printfn "Done"
81+
member _.Combine(a: Async<unit>, b: Async<unit>) = async {
82+
do! a
83+
do! b
8884
}
89-
90-
// To run:
91-
Async.RunSynchronously computation
92-
module Builders =
93-
type EachBuilder(factory: (unit -> Async<unit>) -> EachFunction) =
94-
member _.Zero() = ()
95-
member _.Delay(f: unit -> unit) = fun() -> async { return f() }
96-
member _.Delay(f: unit -> Async<unit>) = f
97-
member _.Run(f: (unit -> Async<unit>)) = factory f
98-
member _.Run (f: unit -> unit) = factory <| fun() -> async { return f() }
85+
member _.Bind(m: Async<'T>, f: 'T -> Async<unit>) = async.Bind(m, f)
86+
member _.Run(f: Async<unit>) = factory (fun() -> f)
9987

10088
type ItBuilder(name: string) =
101-
member _.Zero() = ()
102-
member _.Delay(f: unit -> unit) = fun() -> async { return f() }
103-
member _.Delay(f: unit -> Async<unit>) = f
104-
member _.Run (f: unit -> Async<unit>) = It.Active name f
105-
member _.Run (f: unit -> unit) = It.Active name (fun() -> async { return f() })
89+
member _.Zero() = async { return () }
90+
member _.Return x = async { return x }
91+
member _.ReturnFrom(x: Async<unit>) = x
92+
member _.Delay(f: unit -> Async<unit>) = async.Delay f
93+
member _.Combine(a: Async<unit>, b: Async<unit>) = async {
94+
do! a
95+
do! b
96+
}
97+
member _.Bind(m: Async<'T>, f: 'T -> Async<unit>) = async.Bind(m, f)
98+
member _.Run(f: Async<unit>) = It.Active name (fun () -> f)
10699

107100
type DescribeBuilder(name) =
108101
member _.Zero() = Describe.New name
@@ -139,31 +132,6 @@ let pending name = It.Pending name
139132

140133
let describe name = Builders.DescribeBuilder name
141134

142-
let asyncSuite = describe "Async Tests" {
143-
beforeEach {
144-
let! x = async {
145-
debug "Before each async test"
146-
return 42
147-
}
148-
do! Async.Sleep 100
149-
}
150-
151-
it "should run an async test" {
152-
do! Async.Sleep 100
153-
let! x = async {
154-
debug "Before each async test"
155-
return 42
156-
}
157-
printfn "Async test completed with value: %d" x
158-
}
159-
160-
it "should handle async failure" {
161-
do! Async.Sleep 100
162-
failwith "Intentional async failure"
163-
}
164-
}
165-
166-
167135
module Reporters =
168136
open System.Diagnostics
169137

Tests.fsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,12 @@ let asyncSuite = describe "Async Tests" {
8383
}
8484
}
8585

86-
runTestSuite suite
87-
runTestSuiteWithContext
88-
{ TestContext.New with Reporter = Reporters.TapReporter() }
89-
suite
90-
runTestSuite asyncSuite
86+
[
87+
runTestSuite suite
88+
runTestSuiteWithContext
89+
{ TestContext.New with Reporter = Reporters.TapReporter() }
90+
suite
91+
runTestSuite asyncSuite
92+
]
93+
|> Async.Sequential
94+
|> Async.RunSynchronously

expected.log

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
Running 1 tests...
2-
Main Suite
3-
Suite 1
4-
Suite 2
5-
Before each test in Suite 2
6-
This test passes in Suite 2
7-
 ✅ passed: should pass in Suite 2
8-
All tests passed!
9-
Summary: 1 passed, 0 failed, 0 pending
10-
Total time: 00:00:00.0042054
111
Running 7 tests...
122
A larger test suite
133
Top level info message
@@ -44,7 +34,7 @@ There were 2 test failures:
4434
- A larger test suite / should fail - Intentional failure
4535
- A larger test suite / Arithmetic / Faulty Addition / should fail when adding incorrect numbers - Expected 5 but got 4
4636
Summary: 4 passed, 2 failed, 1 pending
47-
Total time: 00:00:00.0034252
37+
Total time: 00:00:00.0124887
4838
TAP version 14
4939
1..7
5040
ok should pass
@@ -63,3 +53,13 @@ not ok should fail when adding incorrect numbers
6353
severity: fail
6454
...
6555

56+
Running 2 tests...
57+
Async Tests
58+
Before each async test
59+
 ✅ passed: should run an async test
60+
Before each async test
61+
 ❌ failed: should handle async failure - Intentional async failure
62+
There were 1 test failures:
63+
- Async Tests / should handle async failure - Intentional async failure
64+
Summary: 1 passed, 1 failed, 0 pending
65+
Total time: 00:00:00.4115251

out.png

-3.45 KB
Loading

sample.fsx

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,36 @@ open Sprout
44

55
let suite = describe "A test suite" {
66
Info "Top level info message"
7+
8+
// variables may be used to store state across tests
9+
let mutable b = false
10+
beforeEach { b <- true }
711
it "should pass" {
812
info "This test passes"
13+
14+
// simple assertions included out-of-the-box
15+
b |> shouldBeTrue
916
}
1017

1118
it "should fail" {
1219
info "This test fails"
1320
failwith "Intentional failure"
1421
}
1522

23+
// use pending to mark tests that are not yet implemented
1624
pending "This is a pending test"
1725

18-
describe "Nested suite" {
19-
Debug "Use beforeEach and afterEach for setup and teardown"
20-
beforeEach {
21-
debug "Before each test"
22-
}
26+
describe "Async works too" {
27+
Debug "Async test example"
2328

24-
afterEach {
25-
debug "After each test"
26-
}
27-
it "should also pass" {
28-
info "Nested test passes"
29+
// asynchronous flows are supported
30+
it "should run asynchronously" {
31+
do! Async.Sleep 1000
32+
info "Async test completed"
2933
}
3034
}
3135

36+
// use nested suites to organize tests
3237
describe "Arithmetic" {
3338
describe "Addition" {
3439
it "should add two numbers correctly" {
@@ -43,23 +48,28 @@ let suite = describe "A test suite" {
4348
result |> shouldEqual 9
4449
}
4550
}
46-
}
4751

48-
describe "Comparisons" {
49-
debug "Testing comparisons"
50-
it "should compare numbers correctly" {
51-
5 > 3 |> shouldBeTrue
52+
describe "Comparisons" {
53+
debug "Testing comparisons"
54+
it "should compare numbers correctly" {
55+
5 > 3 |> shouldBeTrue
56+
}
5257
}
53-
}
5458

55-
describe "Parameterized Tests" {
56-
info "Simply embed test cases and loop over them"
57-
let numbers = [1; 2; 3; 4; 5]
58-
for n in numbers do
59-
it $"should handle number {n}" {
60-
n > 0 |> shouldBeTrue
61-
}
59+
// parameterized tests are supported using regular F# loops
60+
// type-safe as expected without any special syntax
61+
describe "Parameterized Tests" {
62+
info "Simply embed test cases and loop over them"
63+
let numbers = [1; 2; 3; 4; 5]
64+
for n in numbers do
65+
it $"should handle number {n}" {
66+
n > 0 |> shouldBeTrue
67+
}
68+
}
6269
}
6370
}
6471

65-
runTestSuiteWithContext suite { TestContext.Empty with Reporter = Reporters.ConsoleReporter("v", "x", "?") :> ITestReporter }
72+
runTestSuiteWithContext
73+
{ TestContext.New with Reporter = Reporters.ConsoleReporter("v", "x", "?", " ") :> ITestReporter }
74+
suite
75+
|> Async.RunSynchronously

0 commit comments

Comments
 (0)