Skip to content

Commit

Permalink
Merge pull request #83 from safareli/hook
Browse files Browse the repository at this point in the history
add seq/par and hook
  • Loading branch information
felixmulder committed Mar 20, 2019
2 parents 9b51db3 + 85c503f commit 003ef97
Show file tree
Hide file tree
Showing 33 changed files with 1,639 additions and 753 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# editorconfig.org

This comment has been minimized.

Copy link
@themandelp

themandelp Mar 31, 2019

Test.Spec.Reporter.Console


[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
5 changes: 4 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"url": "git://github.com/purescript-spec/purescript-spec.git"
},
"dependencies": {
"purescript-avar": "^3.0.0",
"purescript-console": "^4.1.0",
"purescript-aff": "^5.0.0",
"purescript-exceptions": "^4.0.0",
Expand All @@ -24,6 +25,8 @@
"purescript-foldable-traversable": "^4.0.0",
"purescript-pipes": "^6.0.0",
"purescript-ansi": "^5.0.0",
"purescript-generics-rep": "^6.0.0"
"purescript-generics-rep": "^6.0.0",
"purescript-fork": "^4.0.0",
"purescript-now": "^4.0.0"
}
}
17 changes: 9 additions & 8 deletions docs/running.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# Running

When you have a spec, you need a runner to actually run it and get the results.
PureScript Spec comes with a NodeJS runner, `run`, which takes an array of
PureScript Spec comes with a NodeJS runner, `runSpec`, which takes an array of
*reporters* and a spec to run. What you get back is a test-running program of
type `Effect ()`. The effect rows in `r` depend on what you do in your specs and
what reporters you are using. The program can be run using
type `Aff Unit`. The program can be run using
[Pulp](https://github.com/bodil/pulp).

```bash
Expand All @@ -25,13 +24,13 @@ After that has finished, you can run the test program using NodeJS.
NODE_PATH=output node -e "require('Test.Main').main();"
```

This comment has been minimized.

Copy link
@themandelp

themandelp Mar 31, 2019

[#Browser-Testing]

This comment has been minimized.

Copy link
@themandelp

themandelp Mar 31, 2019

`run [reporter1, ..., reporterN


**NOTE:** A test program using `Test.Spec.Runner.run` cannot be browserified
**NOTE:** A test program using `Test.Spec.Runner.runSpec` cannot be browserified
and run in the browser, it requires NodeJS. To run your tests in a browser,
see [Browser Testing](#browser-testing) below.

## Reporters

Reporters can be passed to the runner, e.g. `run [reporter1, ..., reporterN]
Reporters can be passed to the runner, e.g. `runSpec [reporter1, ..., reporterN]
spec`. Currently there are these reporters available:

* `consoleReporter` in `Test.Spec.Reporter.Console`
Expand All @@ -42,11 +41,13 @@ spec`. Currently there are these reporters available:

## Passing Runner Configuration

In addition to the regular `run` function, there is also `run'`, which takes a
`Config` record.
In addition to the regular `runSpec` function, there is also `runSpecT`, which also
takes `Config` record. also instead of `Spec Unit` it takes `SpecT Aff Unit m Unit`
and returns `m (Aff (Array (Tree Void Result)))`. if we specialize the `m` to `Identity`
then code will look like this:

```purescript
main = launchAff_ $ run' testConfig [consoleReporter] mySpec
main = launchAff_ $ un Identity $ runSpecT testConfig [consoleReporter] mySpec
where
testConfig = { slow: 5000, timeout: Just 10000, exit: false }
```
Expand Down
196 changes: 192 additions & 4 deletions docs/writing-specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ import Effect.Aff (launchAff_, delay)
import Test.Spec (pending, describe, it)
import Test.Spec.Assertions (shouldEqual)
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner (run)
import Test.Spec.Runner (runSpec)
main :: Effect Unit
main = launchAff_ $ run [consoleReporter] do
main = launchAff_ $ runSpec [consoleReporter] do
describe "purescript-spec" do
describe "Attributes" do
it "awesome" do
Expand Down Expand Up @@ -121,7 +121,7 @@ baseSpecs = do
```

This is often used to combine all specs into a single spec that can be passed
to the test runner, if not using [purescript-spec-discovery](https://github.com/owickstrom/purescript-spec-discovery).
to the test runner, if not using [purescript-spec-discovery](https://github.com/purescript-spec/purescript-spec-discovery).

## Running A Subset of the Specs

Expand All @@ -147,8 +147,196 @@ describe "Module" do
it "does feature Y" ...
```

There is also `focus` which can be used to select some specific group for execution

```purescript
describe "Module" do
describe "Sub Module A"
it "does feature X" ...
focus $ describe "Sub Module B" do -- all tests passed to focus will be executed
it "does feature Y" ...
it "does feature Z" ...
describe "Sub Module C" do
it "does feature P" ...
```



## QuickCheck

You can use [QuickCheck](https://github.com/purescript/purescript-quickcheck)
together with the [purescript-spec-quickcheck](https://github.com/owickstrom/purescript-spec-quickcheck)
together with the [purescript-spec-quickcheck](https://github.com/purescript-spec/purescript-spec-quickcheck)
adapter to get nice output formatting for QuickCheck tests.


## Parallel spec execution

You can use `parallel` to mark specs for parallel execution. This is useful
if you want to speed up your tests by not waiting for some async action
to resolve. so if you have:

```purescript
describe "delay" do
it "proc 1" do
delay $ Milliseconds 500.0
it "proc 2" do
delay $ Milliseconds 500.0
it "proc 3" do
delay $ Milliseconds 1000.0
```

It would take `2000 ms` to finish. But, by sticking in `parallel`, it would take `1000 ms`:

```diff
- describe "delay" do
+ describe "delay" $ parallel do
```

**NOTE** that if you are logging things to console, by using `parallel`
order of log messages is less deterministic. For example if you had:

```purescript
describe "delay" do
it "proc 1" do
log $ "start 1"
delay $ Milliseconds 500.0
log $ "end 1"
it "proc 2" do
log $ "start 2"
delay $ Milliseconds 500.0
log $ "end 2"
it "proc 3" do
log $ "start 3"
delay $ Milliseconds 1000.0
log $ "end 3"
```

you would see messages in this order:

```
start 1
end 1
start 2
end 2
start 3
end 3
```

but if you have used `parallel` then messages will come in this order:

```
start 1
start 2
start 3
end 1
end 2
end 3
```

`purescript-spec` itself is not providing any specific solution for this
issue but you can take a look at [/test/Test/Spec/HoistSpec.purs](https://github.com/purescript-spec/purescript-spec/blob/master/test/Test/Spec/HoistSpec.purs)
for some inspiration.

## Using hooks

`before_` runs a custom action before every spec item. For example, if you
have an action `flushDb` which flushes your database, you can run it before
every spec item with:

```purescript
main :: Spec Unit
main = before_ flushDb do
describe "/api/users/count" do
it "returns the number of users" do
post "/api/users/create" "name=Jay"
get "/api/users/count" `shouldReturn` 1
describe "when there are no users" do
it "returns 0" do
get "/api/users/count" `shouldReturn` 0
```

Similarly, `after_` runs a custom action after every spec item:

```purescript
main :: Spec Unit
main = after_ truncateDatabase do
describe "createUser" do
it "creates a new user" do
let eva = User (UserId 1) (Name "Eva")
createUser eva
getUser (UserId 1) `shouldReturn` eva
describe "countUsers" do
it "counts all registered users" do
countUsers `shouldReturn` 0
```

`around_` is passed an action for each spec item so that it can perform
whatever setup and teardown is necessary.

```purescript
serveStubbedApi :: String -> Int -> Aff Server
stopServer :: Server -> Aff Unit
withStubbedApi :: Aff Unit -> Aff Unit
withStubbedApi action =
bracket (serveStubbedApi "localhost" 80)
stopServer
(const action)
main :: Spec Unit
main = around_ withStubbedApi do
describe "api client" do
it "should authenticate" do
c <- newClient (Just ("user", "pass"))
get c "/api/auth" `shouldReturn` status200
it "should allow anonymous access" do
c <- newClient Nothing
get c "/api/dogs" `shouldReturn` status200
```

Hooks support passing values to spec items (for example, if you wanted
to open a database connection before each item and pass the connection in).
This can be done with `before`, `around` and `after`. Here's an example
for how to use `around`:

```purescript
openConnection :: Aff Connection
openConnection = ...
closeConnection :: Connection -> Aff Unit
closeConnection = ...
withDatabaseConnection :: (Connection -> Aff Unit) -> Aff Unit
withDatabaseConnection = bracket openConnection closeConnection
spec :: Spec Unit
spec = do
around withDatabaseConnection do
describe "createRecipe" do
it "creates a new recipe" $ \c -> do
let ingredients = [Eggs, Butter, Flour, Sugar]
createRecipe c (Recipe "Cake" ingredients)
getRecipe c "Cake" `shouldReturn` ingredients
```

Hooks support nesting too:

```purescript
spec :: Spec Unit
spec = do
before (pure 1) $ after (\a -> a `shouldEqual` 1) do
it "before & after usage" \num -> do
num `shouldEqual` 1
beforeWith (\num -> num `shouldEqual` 1 *> pure true) do
it "beforeWith usage" \bool -> do
bool `shouldEqual` true
aroundWith (\computation bool -> bool `shouldEqual` true *> pure "fiz" >>= computation <* pure unit) do
it "aroundWith usage" \str -> do
str `shouldEqual` "fiz"
beforeWith (\num -> num `shouldEqual` 1 *> pure (show num)) do
it "beforeWith" \str -> do
str `shouldEqual` "1"
```
7 changes: 4 additions & 3 deletions example/Main.purs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
module Main where

import Prelude

import Data.Time.Duration (Milliseconds(..))
import Effect (Effect)
import Effect.Aff (delay, launchAff_)
import Data.Time.Duration (Milliseconds(..))
import Test.Spec (pending, describe, it)
import Test.Spec.Assertions (shouldEqual)
import Test.Spec.Reporter.Console (consoleReporter)
import Test.Spec.Runner (run)
import Test.Spec.Runner (runSpec)

main :: Effect Unit
main = launchAff_ $ run [consoleReporter] do
main = launchAff_ $ runSpec [consoleReporter] do
describe "purescript-spec" do
describe "Attributes" do
it "awesome" do
Expand Down

0 comments on commit 003ef97

Please sign in to comment.