获取其中的函数名称

ДМИ*_*КОВ 7 haskell hunit template-haskell

我有一大堆的功能,如:method1,method2,method3.对于所有的人也有HUnit类似的测试功能:testMethod1,testMethod2,testMethod3.

testMethod1 = TestCase $
  assertEqual "testmethod1" ...

testMethod2 = TestCase $
  assertEqual "testmethod2" ...

testMethod3 = TestCase $
  assertEqual "testmethod3" ...
Run Code Online (Sandbox Code Playgroud)

我想避免将函数名称的冗余复制作为错误消息的前缀,并将其称为:

testMethod1 = TestCase $
  assertEqual_ ...
Run Code Online (Sandbox Code Playgroud)

如何实现(任何"魔术"技巧都被欣赏)?

所以实际上问题是如何在其定义中采用函数名称?


更新.

从原始问题实际上并不清楚,我也想处理这种情况:

tProcess = TestCase $ do
  assertEqual "tProcess" testResult $ someTest
  assertEqual "tProcess" anotherTestResult $ anotherTest
  assertEqual "tProcess" resultAgain $ testAgain
Run Code Online (Sandbox Code Playgroud)

最后我想写一些类似的东西:

tProcess = TestCase $ do
  assertEqual_ testResult $ someTest
  assertEqual_ anotherTestResult $ anotherTest
  assertEqual_ resultAgain $ testAgain
Run Code Online (Sandbox Code Playgroud)

ehi*_*ird 11

你不能直接这样做(即你的测试用例开始testMethodN = ...),但是你可以使用Template Haskell来实现这个目的:

testCase "testMethod1" [| do
    assertEqual_ a b
    assertEqual_ c d
 |]
Run Code Online (Sandbox Code Playgroud)

这涉及编写testCase :: String -> Q Exp -> Q [Dec],将测试用例的名称和引用的表达式转换为声明列表的函数.例如:

{-# LANGUAGE TemplateHaskell #-}
    
import Data.Char
import Control.Applicative
import Control.Monad
import Language.Haskell.TH
import Data.Generics

assertEqual :: (Eq a) => String -> a -> a -> IO ()
assertEqual s a b = when (a /= b) . putStrLn $ "Test " ++ s ++ " failed!"

assertEqual_ :: (Eq a) => a -> a -> IO ()
assertEqual_ = error "assertEqual_ used outside of testCase"

testCase :: String -> Q Exp -> Q [Dec]
testCase name expr = do
    let lowerName = map toLower name
    e' <- [| assertEqual lowerName |]
    pure <$> valD
        (varP (mkName name))
        (normalB (everywhere (mkT (replaceAssertEqual_ e')) <$> expr))
        []
  where
    replaceAssertEqual_ e' (VarE n) | n == 'assertEqual_ = e'
    replaceAssertEqual_ _ e = e
Run Code Online (Sandbox Code Playgroud)

这里的基本思想是生成给定名称的定义,并用assertEqual_引号替换引用表达式中变量的每个匹配项.感谢Template Haskell的Scrap Your Boilerplate支持,我们不需要遍历整个AST,只需为每个节点指定一个转换.assertEqual lowerNameExp

请注意,assertEqual_必须是具有正确类型的绑定标识符,因为引用的表达式在传递给之前已经过类型检查testCase.此外,testCase由于GHC的阶段限制,必须在与其使用的模块不同的模块中定义.

  • 顺便说一句,`assertEqual`来自`Test.HUnit`模块⇒没有必要重写它.并且`testCase`实际上看起来像`testCase'方法"[| TestCase $ do ... |]` (2认同)