A library to mock the current time and relevant
by using a type class.
You can get the great command of the current time in UTC,
time zones, and the speed of time.
module Main where import Control.Monad.TimeMachine import Control.Monad.Trans ( liftIO ) main :: IO () main = backTo (the future) $ do t <- getCurrentTime liftIO . putStrLn $ "We are at " ++ show t -- We are at 1985-10-26 08:24:00.035889 UTC
As you know, time-dependent
IO actions are extremely hard to test.
Assume, for example, the following simple action.
It should return
"Good morning" only in the morning,
thus the results of its unit tests are fatally fragile.
getGreeting :: IO String getGreeting = do t <- getCurrentTime if utctDayTime t <= 12 * 60 * 60 then return "Good morning" else return "Hello"
This library aims to make such actions testable with minimal changes. Actually what you have to do is just:
MonadTimetype class instead of
Here is the testable version of
You can see that nothing is changed excepting the signature.
getGreeting :: (MonadTime m) => m String getGreeting = do t <- getCurrentTime if utctDayTime t <= 12 * 60 * 60 then return "Good morning" else return "Hello"
Mocking the Current Time
travelTo changes the result of
getCurrentTime and relevant actions.
Other than pointing the target
you have two ways to determine how to mock the current time.
This library provides a small DSL to construct the destinations of your time travels.
-- By a date-time according to your local time zone main = travelTo (oct 26 1985 am 1 24) $ do getCurrentTime >>= (liftIO . print)
-- By a relative date main = travelTo (3 `days` ago) $ do getCurrentTime >>= (liftIO . print)
For more detail, see the document.
Mocking Time Zones
jumpTo switch the time zone which is used for calculating the local time.
By this function, you can test time-zone-sensitive actions.
import qualified Data.Time.Zones as TZ main = jumpTo "Asia/Shanghai" $ do t <- getCurrentTime tz <- loadLocalTZ liftIO . print $ TZ.timeZoneForUTCTime tz t -- CST
Mocking the Speed of Time
accelerate changes the speed of time.
In the following example, time flies 60 times faster than the real.
main = accelerate (x 60) $ do getCurrentTime >>= (liftIO . print) -- (*) liftIO . threadDelay $ 1000 * 1000 -- wait a second getCurrentTime >>= (liftIO . print) -- around a minute after (*)
Moreover, as a special case of
halt stops the time.
That is remarkably useful to fix the point of time during your tests.
main = halt $ do getCurrentTime >>= (liftIO . print) -- (*) liftIO . threadDelay $ 1000 * 1000 -- wait a second getCurrentTime >>= (liftIO . print) -- exactly same as (*)
The project is managed by Stack, so you can install it simply:
$ git clone https://github.com/y-taka-23/time-machine.git $ cd time-machine $ stack install
This project is released under the BSD 3-clause license. For more details, see [LICENSE](./LICENSE) file.
*Note that all licence references and agreements mentioned in the time-machine README section above are relevant to that project's source code only.