hspec / hspec

A Testing Framework for Haskell
https://hspec.github.io/
MIT License
747 stars 105 forks source link

fully monadic EDSL #5

Closed gregwebs closed 13 years ago

gregwebs commented 13 years ago

The hspec DSL is already good, but I think it can be improved.

main = hspecX $ do
  describe "hspec's monadic EDSL" $ do
    it "has no commas" $ do -- "$ do" only necessary for multi-line test

    describe "supports nested describes" $ do
      hunit "appends describe text" (text @? "appended")

  describe "supports multiple describes $ do
    ...
trystan commented 13 years ago

I like it, but I'm not sure about changing an already published API or having another way of defining specs. I'll keep this in mind.

trystan commented 13 years ago

I added a "descriptions" function that flattens a list of describes. This sorta kinda helps with multiple describes.

descriptions :: [IO [Spec]] -> IO [Spec]
gregwebs commented 13 years ago

Thanks, descriptions is useful.

I want to declare a reference in my describe that I can share between it blocks. In RSpec I would have created an instance variable in a before block.

In hspec, I have to do the following:

describe "describe" (
  let common = "common"
  in  [
        it "test1" test1,
        it "test2" test2
   ]) 

Now there is an extra pair of parentheses, and I had to adjust my white space. In a monadic EDSL one could add a let binding without any consequences

describe "describe" $ do
   let common = "common"

    it "test1" test1
    it "test2" test2

I wouldn't mind working on this myself- I am just shaving a few other haskell yaks at the moment- maybe in a week or two I will try to tackle it..

trystan commented 13 years ago

This is similar to what is done with the specs for hspec, a set of example specs and it's report is created and shared by many tests.

specs :: IO [Spec]
specs = do
  exampleSpecs <- describe "Example" [
    it "pass" (Success),
    it "fail 1" (Fail "fail message"),
    it "pending" (Pending "pending message"),
    it "fail 2" (HUnit.assertEqual "assertEqual test" 1 (2::Int)),
    it "exceptions" (undefined :: Bool),
    it "quickcheck" (property $ \ i -> i == (i+1::Integer))]

  let report = pureHspec exampleSpecs

  descriptions [

    describe "the \"describe\" function" [
        it "takes a description of what the behavior is for"
            ((=="Example") . name . head $ exampleSpecs),

        it "groups behaviors for what's being described"
            (all ((=="Example").name) exampleSpecs)
    ],
  ... and so on ...

I think we'll eventually have our own set of Haskell idioms to match how things are done in ruby with RSpec that we can put on the wiki page or other documentation.

gregwebs commented 13 years ago

using let outside the describe avoids the need for parentheses and an 'in' with required change in indentation', but it has drawbacks. I can't rebind the let to something different the next describe block (or is it just a shadowing warning?). There are certainly some cases like the one above where it is perfectly fine outside of the descriptions/describe. But normally I want them to be inside the description, not outside, because if you haven't seen the description string (of what the binding is for) you are viewing it outside of any context.

trystan commented 13 years ago

The monadic edsl has been merged into v0.4 but the README, haddock comments, and any examples need to be updated.

gregwebs commented 13 years ago

I think the API should make runSpecM fade away if possible- hspec would call runSpecM.

trystan commented 13 years ago

Yes, it's still a work in progress. I'm still working on the inner plumbing and all that because I want to eventually print the output after each example is evaluated now that the examples no longer write to stdout.

trystan commented 13 years ago

As of v0.4.3, if you import Test.Hspec.Monadic instead of Test.Hspec, you will get the monadic api instead of the list based api.