Get function name inside it

I have a bunch of functions like: method1 , method2 , method3 . For them there are HUnit test functions, such as: testMethod1 , testMethod2 , testMethod3 .

 testMethod1 = TestCase $ assertEqual "testmethod1" ... testMethod2 = TestCase $ assertEqual "testmethod2" ... testMethod3 = TestCase $ assertEqual "testmethod3" ... 

I would like to avoid over-copying the function name as an error prefix and name it something like this:

 testMethod1 = TestCase $ assertEqual_ ... 

How can this be achieved (any "magic" trick appreciated)?

So, the real question is, how can I use the function name inside this definition?


Update .

Actually it is not clear from the original question that I also want to deal with this situation:

 tProcess = TestCase $ do assertEqual "tProcess" testResult $ someTest assertEqual "tProcess" anotherTestResult $ anotherTest assertEqual "tProcess" resultAgain $ testAgain 

Finally, I want to write something like this:

 tProcess = TestCase $ do assertEqual_ testResult $ someTest assertEqual_ anotherTestResult $ anotherTest assertEqual_ resultAgain $ testAgain 
+7
source share
2 answers

You cannot do this directly (i.e. so that the test script testMethodN = ... with testMethodN = ... ), but you can use Template Haskell to get the following:

 testCase "testMethod1" [| do assertEqual_ ab assertEqual_ cd |] 

This includes the entry testCase :: String -> Q Exp -> Q [Dec] , a function to turn the name of the test case and the quoted expression into a list of declarations. For example:

 {-# 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 sab = 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 

The main idea here is to generate a definition of the specified name and replace each occurrence of the variable assertEqual_ in the quoted expression with assertEqual lowerName . Thanks to the support of the Template Haskell Scrap Your Boilerplate, we don’t need to go through the entire AST, just specify the transformation for each Exp node.

Note that assertEqual_ must be the associated identifier with the correct type, because the quoted expression is checked typechecked before passing to testCase . In addition, testCase must be defined in a separate module than the one in which it was used, due to the limitation of the GHC level.

+10
source

Existing answers explain how to do this with metaprogramming, but one way to avoid this problem is to use anonymous tests that take their name as an argument.

Then we can use Data.Map to associate them with their names (in this case, I just use raw Assertions, as well as some syntactic sugar from the map-syntax package):

 import Data.Map import Data.Map.Syntax import Test.HUnit assertEqual_ xyn = assertEqual nxy Right tests = runMap $ do "test1" ## assertEqual_ 1 2 "test2" ## assertEqual_ 1 1 "test3" ## assertEqual_ 3 2 

To run them, we can collapse Data.Map using a function that:

  • It takes a name and a wait-for-name statement as arguments.
  • Skips name to pending-for-name declaration
  • Skips the received Assertion to TestCase
  • Launches TestCase
  • Attaches to another monadic action using >>

We use return () as our default monadic action:

 runTests = foldWithKey go (return ()) tests where go name test = (runTestTT (TestCase (test name)) >>) 

This gives the following results:

 > go ### Failure: test1 expected: 1 but got: 2 Cases: 1 Tried: 1 Errors: 0 Failures: 1 Cases: 1 Tried: 1 Errors: 0 Failures: 0 ### Failure: test3 expected: 3 but got: 2 Cases: 1 Tried: 1 Errors: 0 Failures: 1 
+1
source

All Articles