/specdris

A test framework for Idris

Primary LanguageIdrisMIT LicenseMIT

Build Status

specdris

With this framework you can write spec-like Unit Tests in Idris:

import Specdris.Spec

main : IO ()
main = spec $ do
  describe "This is my math test" $ do
    it "adds two natural numbers" $ do
      (1 + 1) `shouldBe` 2
    it "multiplies two natural numbers" $ do
      (2 * 2) `shouldBe` 3
    it "do fancy stuff with complex numbers" $ do
      pendingWith "do this later"

You can also nest describe. When executed this spec it will produce the following output:

This is my math test
  + adds two natural numbers
  + multiplies two natural numbers
    [x] not equal --red
        actual:   4
        expected: 3
  + do fancy stuff with complex numbers
    [] pending: do this later -- yellow
    
Failed: Total = 3, Failed = 1, Pending = 1 -- red

You can also test your IO code:

import Specdris.SpecIO

main : IO ()
main = specIO $ do
  describe "This is my side effect test" $ do
    it "say my name" $ do
      name <- loadName
      
      pure $ name `shouldBe` "Foo"

You can find more information about SpecIO here.

Both spec and specIO have backend-agnostic versions, respectively spec' and specIO', that use IO' rather than IO.

Install

This testing framework is written with Idris 1.0.

Clone the repo from github with git clone https://github.com/pheymann/specdris.git and run:

cd specdris
./project --install

# under windows
.\Project.ps1 --install

elba

If you use elba to manage your Idris packages, you can also add specdris as a dev-dependency to be run during tests. Just add the following to the [dev_dependencies] section of your package's elba.toml manifest:

[dev_dependencies]
# snip
"pheymann/specdris" = { git = "https://github.com/pheymann/specdris" }

Then you'll be able to use specdris from all test targets.

Documentation

Expectations

Currently this framework provides you with:

Expectation Alias Description
a shouldBe b === is a equal to b
a shouldNotBe b /== is a unequal to b
a shouldBeTrue is a True
a shouldBeFalse is a False
a shouldSatisfy pred satisfies a a given predicate
a shouldBeJust exp if a is Just a' apply exp to a'; here exp is again a sequence of expectations

Failed Test Cases

If an expectations in a test case failes the following expectations aren't executed and the whole case is marked as failure:

  it "failing test" $ do
    1 `shouldBe` 1 -- success
    2 `shouldBe` 1 -- failes
    2 `shouldBe` 2 -- will not be executed

SpecIO

Besides the usual test cases you can also add effects as:

BeforeAll

Executes an IO () before running any test case:

specIO {beforeAll = putStrLn "hello"} $ do ...

AfterAll

Executes an IO () after all test cases are executed:

specIO {afterAll = putStrLn "bye"} $ do ...

Around

Takes a function IO SpecResult -> IO SpecResult which can be used to execute IO code before and after every test case:

around : IO SpecResult -> IO SpecResult
around resultIO = do putStrLn "hello"
                     result <- resultIO
                     putStrLn "bye"
                     
                     pure result

specIO {around = around} $ do

Shuffle/Randomise Test Case Execution

You can randomise your test case execution by applying shuffle to your spec:

mySpec : SpecTree
mySpec = 
  describe "my test"
    it "a" $ 1 === 1
    it "b" $ 2 === 2

-- default seed
spec $ shuffle mySpec

-- static seed
spec $ shuffle mySpec 1000

-- current time
do seed <- System.time
   spec $ shuffle mySpec seed

shuffle only changes execution order for tests (it) under a decribe and thus preserves the logical test structure.

Creating your own Expectations

If you need other expections than these provided here you can implement them as follows:

shouldParse: (Show a, Eq, a) => Parser -> (actual : String) -> (expected : a) -> SpecResult
shouldParse: parser actual expected 
  = case parser actual of
      (Right result) => result === expected
      (Left err)     => UnaryFailure actual $ "couldn't parse: " ++ err

Working with SpecState

It is possible to get the SpecState record of a spec after its execution by using specWithState from Spec or SpecIO.

Storing Console Output in SpecState

If you want to process the output after spec execution you can store it in SpecState by setting spec {storeOutput = True} or specIO {storeOutput = True}. This also stops specdris from printing it to the console.