Add a Test-Suite block to your project Cabal file like below:
Test-Suite projectname-testsuite
Type: exitcode-stdio-1.0
Main-is: TestSuite.hs
Build-depends: base, ....your test framework deps here...
OR:
-- This appears to be contested on ML and in comments below - I have NOT tested this method!
Test-Suite awesomesauce-testsuite
Type: detailed-1.0
Test-Module: Awesomesauce.Tests
Build-depends: base, QuickCheck, HUnit
Note: you might need to run: cabal configure --enable-tests and cabal build again.
Now you should be able to just run: cabal test and it should integrate.
There are two built in test suite Cabal interfaces:
exitcode-stdio-1.0detailed-1.0
The first test suite interface built into Cabal, exitcode-stdio-1.0, depends on the block specifying a Main-is: attribute which is the path of a "main" Haskell file (i.e. a runnable file with a main defined).
The second test suite interface built into Cabal, detailed-1.0, depends on the test-module attribute inside the Test-Suite Cabal block being set to the name of the module to run. This module should export a function named tests with type signature: tests :: [Test]. The Test type can be found in module Distribution.TestSuite. It has two wrapper functions in this module: pure and impure.
Any makers of test frameworks will need to (at a minimum) define instances of the TestOptions and ImpureTestable classes (also exported from Distribution.TestSuite). If the test framework also has a pure runner, you could also define PureTestable class instances for your framework.
You will find cabal packages with name prefixes of cabal-test-XXX if your favorite open source Haskell test library or framework (e.g. QuickCheck, HUnit) is supported already.
quickCheck and those similar functions are run in the IO monad only for printing the result to stdout,
however they will not abort the computation with some exception (e.g. exitFailure).
This is why Cabal reports Pass no matter if the properties passed or not.