I'm currently working in a model-based testing tool. I'm writing some integration tests for it, which require two kind of input:
- Model files: which contain models to be parsed and run by the tool.
- Command files: which contain commands to be input to the tool.
To reduce the boilerplate I've written two functions that build a the path to the model and command files depending on the type of tests. The solution I came up with is shown below. However I don't like the fact that I have to use a dummy parameter in my type-classes.
{-
TorXakis - Model Based Testing
Copyright (c) 2015-2017 TNO and Radboud University
See LICENSE at root directory of this repository.
-}
{-# LANGUAGE OverloadedStrings #-}
-- | Utilities for working with tests paths within 'sqatt'.
module Paths
( BenchTest (BenchTest)
, IntegrationTest (IntegrationTest)
, txsFilePath
, txsCmdPath
)
where
import Data.Text
import Filesystem.Path
import Filesystem.Path.CurrentOS
import Prelude hiding (FilePath)
-- | Types of tests supported by 'sqatt'.
class TestType a where
-- | Directory that contains the model and command files that are used in
-- the tests. This is relative to 'sqatt' root folder.
dataDir :: a -> FilePath
-- | Test of the examples used in the benchmarks.
data BenchTest = BenchTest
instance TestType BenchTest where
dataDir _ = "data" </> "bench"
-- | Integration test.
data IntegrationTest = IntegrationTest
instance TestType IntegrationTest where
dataDir _ = "data" </> "integration-test"
-- | Make a TorXakis model file path. It appends the given directory to the
-- test data directory, and appends the '.txs extension to the given file path.
--
txsFilePath :: TestType a
=> a
-> FilePath -- ^ Directory of the current example.
-> Text -- ^ File name of the current example.
-> FilePath
txsFilePath tt currExampDir fp =
dataDir tt </> currExampDir </> fromText fp <.> "txs"
-- | Make a TorXakis commands file path. It appends the given directory to the
-- test data directory, and appends the .txscmd extension to the given file
-- path.
txsCmdPath :: TestType a
=> a -- ^ Test type. This will determine the location of the test data directory.
-> FilePath -- ^ Directory of the current example.
-> Text -- ^ File name of the current example.
-> FilePath
txsCmdPath tt currExampDir fp =
dataDir tt </> currExampDir </> fromText fp <.> "txscmd"
-
\$\begingroup\$ You could use a dummy parameter of kind Symbol instead and use type application to pass it, that'll allow you not to have to declare a new datatype every time. \$\endgroup\$gallais– gallais2017年10月16日 12:53:37 +00:00Commented Oct 16, 2017 at 12:53
-
1\$\begingroup\$ @gallais Please put all suggestions for improvements in answers. \$\endgroup\$200_success– 200_success2017年10月16日 16:22:10 +00:00Commented Oct 16, 2017 at 16:22
1 Answer 1
Your code is fine and contains documentation, so there's not much to say at that point. That being said, your TestType
problem can be tackled by transforming the type class to a data type:
data TestType = BenchTest | IntegrationTest
dataDir :: TestType -> FilePath
However, that just removes the constraint and makes your program less flexible in the long run. So let us go back to the drawing board. The data directory is part of the test configuration. That sounds like a valid new data type:
data TestConfiguration = TestConfiguration
{ dataDir :: FilePath
, ...
}
defaultConfiguration :: TestConfiguration
defaultConfiguration = TestConfiguration
{ dataDir = "data" </> "test"
, ...
}
Your two functions can now use TestConfiguration
as first parameter instead of a TestType
. This lets your user define adhoc configurations easily:
myConfiguration = defaultConfiguration
{ dataDir = "data" </> "my" </> "tuesday"
, verbosity = VerboseLow
, logFormat = TimeStamps
, ... -- you get it
}
Whether you want to allow record syntax, lenses or setters is left to you.