我正在开发的Cabal包中没有任何意义,我将问题归结为以下示例:
我有以下简单的测试模块:
module Main where
import Test.QuickCheck (quickCheck)
main = quickCheck False
以下Cabal文件位于同一目录中:
name: project
version: 0.1.0.0
cabal-version: >= 1.10
build-type: Simple
executable project
main-is: Main.hs
build-depends: base, QuickCheck
default-language: Haskell2010
test-suite project-test
type: exitcode-stdio-1.0
main-is: Main.hs
build-depends: base, QuickCheck
default-language: Haskell2010
目录中唯一的其他文件是dist(由Cabal在构建时创建)和cabal沙箱文件。可执行文件和测试套件都引用Main.hs,因此我希望在运行“cabal run”时获得与运行“cabal test”时相同的测试结果。然而,显然情况并非如此。
“cabal run”给出:
Preprocessing executable 'project' for project-0.1.0.0...
Running project...
*** Failed! Falsifiable (after 1 test):
这是有道理的,因为属性测试'quickCheck False'应该失败。正如预期的那样,这与我在ghci中运行main时获得的结果相同。
然而,“阴谋测试”给出了:Test suite project-test: RUNNING...
Test suite project-test: PASS
Test suite logged to: dist/test/project-0.1.0.0-project-test.log
1 of 1 test suites (1 of 1 test cases) passed.
为什么“cabal测试”通过测试用例,而“cabal run”按预期失败?
答案 0 :(得分:6)
quickCheck
不会退出程序,因此,它不会设置退出代码。毕竟,您可以进行多次quickCheck
测试,这些测试应该相互独立:
main = do
quickCheck prop1 -- should this exit if the test fails?
quickCheck prop2 -- should this exit if the test fails?
quickCheck prop3 -- should this exit if the test fails?
但是,如果你a)在你的一个测试没有通过时退出,或者b)记住是否没有通过一个测试,然后使用正确的代码退出,你就可以轻松解决这个问题。
为此,您只需使用quickCheckResult
,您可以isSuccess
与Test.QuickCheck.Test
进行核对。如果要使用当前的quickCheck
定义,可以使用限定包含来与特殊实现交换默认实现:
import Control.Monad (when)
import System.Exit (exitFailure)
import Test.QuickCheck hiding (quickCheck, quickCheckWith)
import qualified Test.QuickCheck.Test as Q
quickCheckWith :: Testable prop => Args -> prop -> IO ()
quickCheckWith args p = do
success <- fmap Q.isSuccess $ quickCheckWithResult args p
when (not success) $ exitFailure -- exit if the result is a failure
quickCheck :: Testable prop => prop -> IO ()
quickCheck p = quickCheckWith stdArgs p
你应该使用另一个名字,特别是如果其他人在同一个项目上工作。 checkOrExit
似乎是合理的。
这基本相同,但运行所有测试。您必须再次使用quickCheckResult
或quickCheckWithResult
:
import Control.Monad (when)
import System.Exit (exitFailure)
import Test.QuickCheck (quickCheckResult)
import Test.QuickCheck.Test (isSuccess)
main :: IO ()
main = do
let tests = [ quickCheckResult prop1
, quickCheckResult prop2
, quickCheckResult prop3
, quickCheckResult prop4
]
success <- fmap (all isSuccess) . sequence $ tests
when (not success) $ exitFailure
虽然quickCheck
非常适合进行属性检查,但它并不提供完整的测试框架。这就是tasty
或hspec
等其他完整框架派上用场的地方。他们可以采用Testable a
并相应地检查QuickCheck的结果。使用hspec
的示例:
module Main where
import Test.Hspec
import Test.QuickCheck (property)
main = hspec $ do
describe "<method you would like to test>" $
it "<property/assumption you would like to test>" $
property $ False -- quickCheck
这给出了(更详细的)输出
<method you would like to test>
- <property/assumption you would like to test> FAILED [1]
1) <method you would like to test> <property/assumption you would like to test>
Falsifiable (after 1 test):
[remark: the used values would be here, but `False` is a boolean]
Randomized with seed 2077617428
Finished in 0.0019 seconds
1 example, 1 failure
此外,它会以错误代码退出,因此您的cabal test
会正确识别此失败的测试。这些测试框架还具有其他功能,超出了本答案的范围。
QuickCheck不会退出您的程序,但Cabal 仅检查退出代码以确定测试是否已通过。由于任何定期结束的Haskell程序返回零(也就是没有错误),您需要使用exitFailure
(或类似的东西),或者在幕后使用exitFailure
的框架。
请注意,这仅适用于type: exitcode-stdio-1.0
的测试。