Hackage: http://hackage.haskell.org/package/test-framework-golden
GitHub: https://github.com/feuerbach/test-framework-golden
Golden tests are similar to unit tests (as implemented in HUnit), but the idea
is to store the expected result in files (called «golden» files).
I was introduced to the idea of golden testing by Bohdan Vlasyuk at ZuriHac in
2010. Since then I've discovered that this is a nice approach and it is already
used in variety of projects (e.g. ghc, haddock).
Surprisingly, not much is written about golden testing, and I've been unable to
find any golden testing libraries for Haskell or any other programming language.
Every project has its own ad-hoc implementation in Haskell/Python/Shell/etc.
The closest match on the market is Simon Michael's shelltestrunner. But to use
it you have to expose the tested functionality via command line, which may be
inconvenient.
So this is my attempt at a general golden testing library.
It consists of two modules.
Test.Golden has a simple interface that helps you get started very quickly.
Test.Golden.Advanced provides a very generic testing function that you can use
to implement the testing system you dream about.
The library is integrated with test-framework, so you can use golden tests in
addition to SmallCheck/QuickCheck/HUnit tests.
In future there's a plan to support some golden test management capabilities.
Roman
_______________________________________________
Haskell-Cafe mailing list
Haskel...@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe
Sent from my iPad
> I am glad to announce the first public release of
> test-framework-golden — a golden testing library.
Nice!
> The library is integrated with test-framework, so you can use golden
> tests in addition to SmallCheck/QuickCheck/HUnit tests.
I would suggest to rename the modules to
Test.Framework.Golden
and
Test.Framework.Golden.Advanced
to more clearly represent that fact.
Cheers,
Simon
According to the test-framework convention, they ought to be named
Test.Framework.Providers.Golden
and
Test.Framework.Providers.Golden.Advanced
respectively, and you can see that it was the case in the early
versions:
http://hackage.haskell.org/package/test-framework-golden-1.0
But it's just too much to type.
My justification (which you may or may not buy) is that, unlike, say,
Test.Framework.Providers.HUnit, this is not an adaptation of an existing
testing library to test-framework, but is a new library that just
happens to use test-framework. So it's more like Test.HUnit, although it
already subsumes what would become Test.Framework.Providers.Golden.
Roman
Sorry, I'm not sure I understand the question. Links about what?
The library documentation is available on Hackage (the first link
below).
If you're asking about TDD in Haskell in general, I doubt I can tell
you anything that Google can't.
Roman
The paths are naturally relative to the working directory of the test
executable.
In my projects, I typically have
./test.hs
./tests/case1.golden
./tests/case2.golden
./tests/case3.golden
And so test.hs refers to test cases by relative paths like
"tests/case1.golden". Of course, that'll only work if you run test.hs
from its directory, but it is the case most of the time anyway.
Also, since paths are relative to the directory containing the project.cabal
file, "cabal test" works, too.
If, for some reason, you want to be able to run tests from any location,
you could provide the path to golden paths e.g. via an environment
variable.
Roman
*I* think that it might be a good idea to separete your library into two parts: the golden
functionality itself and test-framework provider. Right now it seems impossible to use your
library outside of test-framework. For me that's not a big deal, cause I use test-framework
anyway, but I suspect you could get more users that way.
> According to the test-framework convention, they ought to be named
>
> Test.Framework.Providers.Golden
>
> and
>
> Test.Framework.Providers.Golden.Advanced
>
> respectively, and you can see that it was the case in the early
> versions:
> http://hackage.haskell.org/package/test-framework-golden-1.0
>
> But it's just too much to type.
Well, I'm already typing Test.Framework.Providers.QuickCheck2, so that's not a big deal really :)
Jan
However, an important part of functionality isn't there at the moment —
golden file management. You should be able to say, "for this test,
take its current output and write it to the corresponding golden file".
In order to do that, you need to have access to the list of golden tests
in the suite. This is where implementation details of different test
frameworks start to matter. Probably one can make an abstraction over
test frameworks that would give the list of all golden tests.
(Although when you start abstracting over test frameworks, which are
abstractions themselves, it becomes somewhat funny.)
Speaking of such functionality, correct me if I'm wrong, but neither
HUnit nor hspec won't be able to support it anyway, because they
represent tests as opaque IO actions.
Nor can HTF provide such support — although its TestSort type could be
extended to indicate that the test is a golden test, still there's no
way to get hold of the golden file name.
test-framework will support this once the Typeable constraint is added
for tests.
Roman
* Simon Hengel <s...@typeful.net> [2012-10-05 13:43:57+0200]
Ok, makes sense.
I'm looking forward to give it a try, and see how it compares to using
operating system primitives (say `cp') for "golden file management".
> Speaking of such functionality, correct me if I'm wrong, but neither
> HUnit nor hspec won't be able to support it anyway, because they
> represent tests as opaque IO actions.
It would be easy to extend Hspec to support this in the same way you
extend test-framework to support this. It requires existentials; the
only substantial difference that I can see is that test-framework
already uses existentials, while Hspec does not.
1. You often want to update not just one test, but all, or some of the
tests (when you've made a change and verified that the changes in
output are expected). Doing it in command line is certainly possible,
but not trivial nor convenient.
2. For some tests (like goldenVsString) the output is not captured in a
file, so using cp directly is not possible.
> > Speaking of such functionality, correct me if I'm wrong, but neither
> > HUnit nor hspec won't be able to support it anyway, because they
> > represent tests as opaque IO actions.
>
> It would be easy to extend Hspec to support this in the same way you
> extend test-framework to support this. It requires existentials; the
> only substantial difference that I can see is that test-framework
> already uses existentials, while Hspec does not.
Well, if you are willing to make this change, then I'll try to do my
part of the job and expose a useful abstraction.
Roman
Yes and yes. I have no fixed expectations nor any idea how an "ideal"
interface would look like, but I guess there is room for improvement.
So I'm really looking forward to try it ;)
> > > Speaking of such functionality, correct me if I'm wrong, but neither
> > > HUnit nor hspec won't be able to support it anyway, because they
> > > represent tests as opaque IO actions.
> >
> > It would be easy to extend Hspec to support this in the same way you
> > extend test-framework to support this. It requires existentials; the
> > only substantial difference that I can see is that test-framework
> > already uses existentials, while Hspec does not.
>
> Well, if you are willing to make this change, then I'll try to do my
> part of the job and expose a useful abstraction.
If it gives us something that is useful from a users perspective, I'm
happy to make that change. I would hope that something like [1] works,
e.g.:
instance Example GoldenTest where
evaluateExample c = evaluateExample c . goldenTestToHUnitAssetion
exampleMetadata = Just . Metadata
That way the Typeable instance is optional.
Personally, I still think that it may be a good idea to first explore
the design space with test-framework before trying to abstract over it.
Cheers,
Simon
[1] https://github.com/sol/hspec/commit/6927f642aea44803b57c2b77548931f6865b0c38