Skip to content

Commit 5bca271

Browse files
authored
Refactor Haskell tests to use tasty (#1577)
1 parent 94b084c commit 5bca271

15 files changed

+131
-123
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
### 🔧 Internal changes
1616

1717
- Refactored navigation bar into a React component (for the graph, grid and generate pages only - the about page navigation bar is still rendered using Blaze)
18+
- Refactored backend tests to use `tasty` and `tasty-hunit` instead of `HUnit`
1819

1920
## [0.7.1] - 2025-06-16
2021

backend-test/Controllers/ControllerTests.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ Module that contains the test suites for all the controllers.
88
module Controllers.ControllerTests
99
( controllerTests ) where
1010

11-
import Test.HUnit (Test (..))
11+
import Test.Tasty (TestTree, testGroup)
1212

1313
import Controllers.CourseControllerTests (courseControllerTestSuite)
1414
import Controllers.GraphControllerTests (graphControllerTestSuite)
1515

1616
-- Single test encompassing all controller test suites
17-
controllerTests :: Test
18-
controllerTests = TestList [courseControllerTestSuite, graphControllerTestSuite]
17+
controllerTests :: TestTree
18+
controllerTests = testGroup "Controller" [courseControllerTestSuite, graphControllerTestSuite]

backend-test/Controllers/CourseControllerTests.hs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import qualified Data.Map as Map
1717
import Data.Maybe (fromMaybe)
1818
import qualified Data.Text as T
1919
import Database.Persist.Sqlite (SqlPersistM, insert_)
20-
import Database.Tables (Courses(..))
20+
import Database.Tables (Courses (..))
2121
import Happstack.Server (rsBody)
22-
import Test.HUnit (Test(..), assertEqual)
23-
import TestHelpers (clearDatabase, runServerPart, runServerPartWithQuery, runServerPartWithCourseInfoQuery)
22+
import Test.Tasty (TestTree, testGroup)
23+
import Test.Tasty.HUnit (assertEqual, testCase)
24+
import TestHelpers (clearDatabase, runServerPart, runServerPartWithCourseInfoQuery,
25+
runServerPartWithQuery)
2426

2527
-- | List of test cases as (input course name, course data, expected JSON output)
2628
retrieveCourseTestCases :: [(String, T.Text, Map.Map T.Text T.Text, String)]
@@ -56,9 +58,9 @@ retrieveCourseTestCases =
5658
]
5759

5860
-- | Run a test case (case, input, expected output) on the retrieveCourse function.
59-
runRetrieveCourseTest :: String -> T.Text -> Map.Map T.Text T.Text -> String -> Test
61+
runRetrieveCourseTest :: String -> T.Text -> Map.Map T.Text T.Text -> String -> TestTree
6062
runRetrieveCourseTest label courseName courseData expected =
61-
TestLabel label $ TestCase $ do
63+
testCase label $ do
6264
let currCourseName = fromMaybe "" $ Map.lookup "name" courseData
6365

6466
let videoUrls = case Map.lookup "videoUrls" courseData of
@@ -89,7 +91,7 @@ runRetrieveCourseTest label courseName courseData expected =
8991
assertEqual ("Unexpected response body for " ++ label) expected actual
9092

9193
-- | Run all the retrieveCourse test cases
92-
runRetrieveCourseTests :: [Test]
94+
runRetrieveCourseTests :: [TestTree]
9395
runRetrieveCourseTests = map (\(label, courseName, courseData, expected) -> runRetrieveCourseTest label courseName courseData expected) retrieveCourseTestCases
9496

9597
-- | Helper function to insert courses into the database
@@ -108,9 +110,9 @@ indexTestCases =
108110
]
109111

110112
-- | Run a test case (case, input, expected output) on the index function.
111-
runIndexTest :: String -> [T.Text] -> String -> Test
113+
runIndexTest :: String -> [T.Text] -> String -> TestTree
112114
runIndexTest label courses expected =
113-
TestLabel label $ TestCase $ do
115+
testCase label $ do
114116
runDb $ do
115117
clearDatabase
116118
insertCourses courses
@@ -119,12 +121,12 @@ runIndexTest label courses expected =
119121
assertEqual ("Unexpected response body for " ++ label) expected actual
120122

121123
-- | Run all the index test cases
122-
runIndexTests :: [Test]
124+
runIndexTests :: [TestTree]
123125
runIndexTests = map (\(label, courses, expected) -> runIndexTest label courses expected) indexTestCases
124126

125127
-- | List of test cases as (case, database state, input [dept], expected JSON output) for the courseInfo function
126128
courseInfoTestCases :: [(String, [Courses], T.Text, String)]
127-
courseInfoTestCases =
129+
courseInfoTestCases =
128130
[ ("Empty Database"
129131
, []
130132
, "STA"
@@ -183,11 +185,11 @@ courseInfoTestCases =
183185
, coursesCoreqs = Nothing
184186
, coursesVideoUrls = []
185187
}
186-
188+
187189
-- | Run a test case (case, database state, input [dept], expected JSON output) on the courseInfo function
188-
runCourseInfoTest :: String -> [Courses] -> T.Text -> String -> Test
190+
runCourseInfoTest :: String -> [Courses] -> T.Text -> String -> TestTree
189191
runCourseInfoTest label state dept expected =
190-
TestLabel label $ TestCase $ do
192+
testCase label $ do
191193
runDb $ do
192194
clearDatabase
193195
mapM_ insert_ state
@@ -196,10 +198,10 @@ runCourseInfoTest label state dept expected =
196198
assertEqual ("Unexpected response body for " ++ label) expected actual
197199

198200
-- | Run all courseInfo test cases
199-
runCourseInfoTests :: [Test]
201+
runCourseInfoTests :: [TestTree]
200202
runCourseInfoTests = map (\(label, state, dept, expected) -> runCourseInfoTest label state dept expected) courseInfoTestCases
201203

202204

203205
-- | Test suite for Course Controller Module
204-
courseControllerTestSuite :: Test
205-
courseControllerTestSuite = TestLabel "Course Controller tests" $ TestList (runRetrieveCourseTests ++ runIndexTests ++ runCourseInfoTests)
206+
courseControllerTestSuite :: TestTree
207+
courseControllerTestSuite = testGroup "Course Controller tests" (runRetrieveCourseTests ++ runIndexTests ++ runCourseInfoTests)

backend-test/Controllers/GraphControllerTests.hs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import qualified Data.Text as T
1616
import Database.Persist.Sqlite (SqlPersistM, insert_)
1717
import Database.Tables (Graph (..))
1818
import Happstack.Server (rsBody)
19-
import Test.HUnit (Test (..), assertEqual)
19+
import Test.Tasty (TestTree, testGroup)
20+
import Test.Tasty.HUnit (assertEqual, testCase)
2021
import TestHelpers (clearDatabase, runServerPart)
2122

2223
-- | List of test cases as (label, input graphs, expected output)
@@ -44,9 +45,9 @@ insertGraphs = mapM_ insertGraph
4445
insertGraph title = insert_ (Graph title 0 0 False )
4546

4647
-- | Run a test case (case, input, expected output) on the index function.
47-
runIndexTest :: String -> [T.Text] -> String -> Test
48+
runIndexTest :: String -> [T.Text] -> String -> TestTree
4849
runIndexTest label graphs expected =
49-
TestLabel label $ TestCase $ do
50+
testCase label $ do
5051
runDb $ do
5152
clearDatabase
5253
insertGraphs graphs
@@ -55,9 +56,9 @@ runIndexTest label graphs expected =
5556
assertEqual ("Unexpected response body for " ++ label) expected actual
5657

5758
-- | Run all the index test cases
58-
runIndexTests :: [Test]
59+
runIndexTests :: [TestTree]
5960
runIndexTests = map (\(label, graphs, expected) -> runIndexTest label graphs expected) indexTestCases
6061

6162
-- | Test suite for Graph Controller Module
62-
graphControllerTestSuite :: Test
63-
graphControllerTestSuite = TestLabel "Graph Controller tests" $ TestList runIndexTests
63+
graphControllerTestSuite :: TestTree
64+
graphControllerTestSuite = testGroup "Graph Controller tests" runIndexTests

backend-test/Database/CourseQueriesTests.hs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import Control.Monad.IO.Class (liftIO)
1414
import qualified Data.Text as T
1515
import Data.Time (getCurrentTime)
1616
import Database.CourseQueries (reqsForPost)
17-
import Database.DataType (PostType(..))
17+
import Database.DataType (PostType (..))
1818
import Database.Persist.Sqlite (insert_)
19-
import Database.Tables (Post(..))
20-
import Test.HUnit (Test(..), assertEqual)
19+
import Database.Tables (Post (..))
20+
import Test.Tasty (TestTree, testGroup)
21+
import Test.Tasty.HUnit (assertEqual, testCase)
2122
import TestHelpers (clearDatabase)
2223

2324
-- | List of test cases as (label, requirements to insert, input program, expected output)
@@ -29,9 +30,9 @@ reqsForPostTestCases =
2930
]
3031

3132
-- | Run a test case (case, requirements, input, expected output) on the reqsForPost function.
32-
runReqsForPostTest :: String -> T.Text -> T.Text -> String -> Test
33+
runReqsForPostTest :: String -> T.Text -> T.Text -> String -> TestTree
3334
runReqsForPostTest label reqsToInsert program expected =
34-
TestLabel label $ TestCase $ do
35+
testCase label $ do
3536
currentTime <- liftIO getCurrentTime
3637
let testPost = Post Major "Computer Science" program "Sample post description" reqsToInsert currentTime currentTime
3738

@@ -44,9 +45,9 @@ runReqsForPostTest label reqsToInsert program expected =
4445
assertEqual ("Unexpected response body for " ++ label) expected actual
4546

4647
-- | Run all the reqsForPost test cases
47-
runReqsForPostTests :: [Test]
48+
runReqsForPostTests :: [TestTree]
4849
runReqsForPostTests = map (\(label, reqsToInsert, program, expected) -> runReqsForPostTest label reqsToInsert program expected) reqsForPostTestCases
4950

5051
-- | Test suite for CourseQueries Module
51-
courseQueriesTestSuite :: Test
52-
courseQueriesTestSuite = TestLabel "Course Queries tests" $ TestList runReqsForPostTests
52+
courseQueriesTestSuite :: TestTree
53+
courseQueriesTestSuite = testGroup "Course Queries tests" runReqsForPostTests

backend-test/Database/DatabaseTests.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ Module that contains the test suites for all the database functions.
88
module Database.DatabaseTests
99
( databaseTests ) where
1010

11-
import Test.HUnit (Test (..))
1211
import Database.CourseQueriesTests (courseQueriesTestSuite)
12+
import Test.Tasty (TestTree, testGroup)
1313

1414
-- Single test encompassing all database test suites
15-
databaseTests :: Test
16-
databaseTests = TestList [courseQueriesTestSuite]
15+
databaseTests :: TestTree
16+
databaseTests = testGroup "Database" [courseQueriesTestSuite]

backend-test/Main.hs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,26 @@ Module that acts as interface for testing multiple test suites using cabal.
77

88
module Main where
99

10-
import Control.Monad (when)
1110
import Config (databasePath)
12-
import Data.Text (unpack)
13-
import Database.Database(setupDatabase)
14-
import System.Directory (removeFile)
15-
import System.Environment (setEnv, unsetEnv)
16-
import qualified System.Exit as Exit
17-
import Test.HUnit (Test (..), failures, runTestTT)
18-
import RequirementTests.RequirementTests (requirementTests)
11+
import Control.Monad (when)
1912
import Controllers.ControllerTests (controllerTests)
13+
import Data.Text (unpack)
14+
import Database.Database (setupDatabase)
2015
import Database.DatabaseTests (databaseTests)
16+
import RequirementTests.RequirementTests (requirementTests)
2117
import SvgTests.SvgTests (svgTests)
22-
23-
tests :: IO Test
24-
tests = do
25-
return $ TestList [requirementTests, controllerTests, svgTests, databaseTests]
18+
import System.Directory (removeFile)
19+
import System.Environment (setEnv, unsetEnv)
20+
import Test.Tasty
2621

2722
main :: IO ()
2823
main = do
2924
setEnv "APP_ENV" "test"
3025
setupDatabase
31-
testSuites <- tests
32-
count <- runTestTT testSuites
33-
when (failures count > 0) Exit.exitFailure
26+
defaultMain tests
3427
path <- databasePath
3528
removeFile $ unpack path
3629
unsetEnv "APP_ENV"
30+
31+
tests :: TestTree
32+
tests = testGroup "Tests" [controllerTests, databaseTests, requirementTests, svgTests]

backend-test/RequirementTests/ModifierTests.hs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ module RequirementTests.ModifierTests
1010

1111
import Database.Requirement
1212
import DynamicGraphs.GraphNodeUtils (concatModOr, stringifyModAnd)
13-
import Test.HUnit (Test (..), assertEqual)
13+
import Test.Tasty (TestTree, testGroup)
14+
import Test.Tasty.HUnit (assertEqual, testCase)
1415

1516
-- Function to facilitate test case creation given a string, Req tuple
16-
createTest :: (Eq a, Show a) => (a -> String) -> String -> [(a, String)] -> Test
17-
createTest function label input = TestLabel label $ TestList $ map (\(x, y) ->
18-
TestCase $ assertEqual ("for (" ++ y ++ ")")
19-
y (function x)) input
17+
createTest :: (Eq a, Show a) => (a -> String) -> String -> [(a, String)] -> TestTree
18+
createTest function label input = testGroup label $ zipWith (\(x :: Int) (y, z) ->
19+
testCase ("Test " ++ show x) $ assertEqual ("for (" ++ z ++ ")")
20+
z (function y)) [0..] input
2021

2122
-- Global FCEs value so the expected output has the same FCEs as the partial function in createTest
2223
globalFces :: Float
@@ -47,15 +48,15 @@ modandModOrInputs = [
4748
, ([ModOr [Level "300", Level "400"], ModOr [Department "CSC", Department "BCB"], Requirement (Raw "some raw text")], show globalFces ++ " CSC/BCB FCEs at the 300/400 level from some raw text")
4849
]
4950

50-
concatModOrTests :: Test
51+
concatModOrTests :: TestTree
5152
concatModOrTests = createTest concatModOr "joining ModOr with a delimiter" concatModOrInputs
5253

53-
simpleModAndTests :: Test
54+
simpleModAndTests :: TestTree
5455
simpleModAndTests = createTest (stringifyModAnd globalFces) "ModAnd not containing ModOrs" simpleModAndInputs
5556

56-
modandModOrTests :: Test
57+
modandModOrTests :: TestTree
5758
modandModOrTests = createTest (stringifyModAnd globalFces) "ModAnd containing ModOrs" modandModOrInputs
5859

5960
-- functions for running tests in REPL
60-
modifierTestSuite :: Test
61-
modifierTestSuite = TestLabel "ReqParser tests" $ TestList [concatModOrTests, simpleModAndTests, modandModOrTests]
61+
modifierTestSuite :: TestTree
62+
modifierTestSuite = testGroup "ReqParser tests" [concatModOrTests, simpleModAndTests, modandModOrTests]

backend-test/RequirementTests/PostParserTests.hs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ module RequirementTests.PostParserTests
1111
import Data.Bifunctor (second)
1212
import qualified Data.Text as T
1313
import Database.DataType (PostType (..))
14-
import Test.HUnit (Test (..), assertEqual)
14+
import Test.Tasty (TestTree, testGroup)
15+
import Test.Tasty.HUnit (assertEqual, testCase)
1516
import qualified Text.Parsec as Parsec
1617
import WebParsing.PostParser (getPostType, postInfoParser)
1718

1819
-- Function to facilitate test case creation given a string, Req tuple
19-
createTest :: (Show a, Eq a, Show b, Eq b) => (a -> b) -> String -> [(a, b)] -> Test
20-
createTest function label input = TestLabel label $ TestList $ map (\(x, y) ->
21-
TestCase $ assertEqual ("for (" ++ show x ++ "),")
22-
y (function x)) input
20+
createTest :: (Show a, Eq a, Show b, Eq b) => (a -> b) -> String -> [(a, b)] -> TestTree
21+
createTest function label input = testGroup label $ zipWith (\(x :: Int) (y, z) ->
22+
testCase ("Test " ++ show x) $ assertEqual ("for (" ++ show y ++ "),")
23+
z (function y)) [0..] input
2324

2425
-- | Input and output pair of each post
2526
-- | Output is in the order of (postDepartment, postCode, postName)
@@ -69,12 +70,12 @@ getPostTypeInputs = [
6970
, (("", "Certificate in Business"), Certificate)
7071
]
7172

72-
postInfoTests :: Test
73+
postInfoTests :: TestTree
7374
postInfoTests = createTest (Parsec.parse postInfoParser "") "Post requirements" $ map (second Right) postInfoInputs
7475

75-
getPostTypeTests :: Test
76+
getPostTypeTests :: TestTree
7677
getPostTypeTests = createTest (uncurry getPostType) "Post requirements" getPostTypeInputs
7778

7879
-- functions for running tests in REPL
79-
postTestSuite :: Test
80-
postTestSuite = TestLabel "PostParser tests" $ TestList [postInfoTests, getPostTypeTests]
80+
postTestSuite :: TestTree
81+
postTestSuite = testGroup "PostParser tests" [postInfoTests, getPostTypeTests]

backend-test/RequirementTests/PreProcessingTests.hs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ module RequirementTests.PreProcessingTests
77
( preProcTestSuite ) where
88

99
import Data.Text as T hiding (map)
10-
import Test.HUnit (Test (..), assertEqual)
10+
import Test.Tasty (TestTree, testGroup)
11+
import Test.Tasty.HUnit (assertEqual, testCase)
1112
import Text.HTML.TagSoup (Tag (..))
1213
import WebParsing.PostParser (pruneHtml)
1314

14-
createTest :: (Eq a, Show a, Eq b, Show b) => (a -> b) -> String -> [(a, b)] -> Test
15-
createTest function label input = TestLabel label $ TestList $ map (\(x, y) ->
16-
TestCase $ assertEqual ("for (" ++ show y ++ ")")
17-
y (function x)) input
15+
createTest :: (Eq a, Show a, Eq b, Show b) => (a -> b) -> String -> [(a, b)] -> TestTree
16+
createTest function label input = testGroup label $ Prelude.zipWith (\(x :: Int) (y, z) ->
17+
testCase ("Test " ++ show x) $ assertEqual ("for (" ++ show z ++ ")")
18+
z (function y)) [0..] input
1819

1920

2021
pruneHtmlInputs :: [([Tag T.Text], [Tag T.Text])]
@@ -33,9 +34,9 @@ pruneHtmlInputs = [
3334
)
3435
]
3536

36-
pruneHtmlTests :: Test
37+
pruneHtmlTests :: TestTree
3738
pruneHtmlTests = createTest pruneHtml "filtering out html attributes" pruneHtmlInputs
3839

3940
-- functions for running tests in REPL
40-
preProcTestSuite :: Test
41-
preProcTestSuite = TestLabel "Pre-processing tests" $ TestList [pruneHtmlTests]
41+
preProcTestSuite :: TestTree
42+
preProcTestSuite = testGroup "Pre-processing tests" [pruneHtmlTests]

0 commit comments

Comments
 (0)