I'm having second thoughts with this solution, so @gbataille if you can discuss it I'll be really happy.
This solution is great because:
it does not change a lot of code
will help us writing real tests asap
This solution sucks because:
Everything is still working in IO, so effectively our code can do anything which may not be enforced / checked by the test. I don't really care about that one, except for the next point
Difficult to make the function returns something else than an Aeson Value (typeclass polymorphism does not live well inside structs).
readFile and putStrLn calls in Krank cannot be mocked.
I can also add something for readFile and putStrLn in the configuration. This way krank will still live in IO, but everything will use customization functions and will be fully testable.
Another solution may be to move to a full "effect" library pattern. However I can see the following effects, currently managed by IO, but that which will need to be detailed:
Network queries via req (Exactly what I'm changing here)
Exception throwing in IO
Parallel computation with mapConcurrently
file reading with readFile
output with putStrLn.
It will be a more important change, which can take the following shape:
a full KrankMonad with all theses operations defined, with one instance for ReaderT KrankConfig IO (i.e. the current implementation) and one for tests (something such as ReaderT (Environment, KrankConfig) (WriterT Outputs (EitherT Exception Identity))).
A mtl like implementation where each of the listed effect are implemented
A free monad
Any other effect library. I'm tempted to play with polysemy, but without real conviction.
With theses change, the code executed to run a rest request is configurable. It means that we can now run test with stub request (not yet done).
For example, we can now write a request function for tests like:
or
I'm having second thoughts with this solution, so @gbataille if you can discuss it I'll be really happy.
This solution is great because:
This solution sucks because:
IO
, so effectively our code can do anything which may not be enforced / checked by the test. I don't really care about that one, except for the next pointValue
(typeclass polymorphism does not live well inside structs).readFile
andputStrLn
calls inKrank
cannot be mocked.I can also add something for
readFile
andputStrLn
in the configuration. This way krank will still live inIO
, but everything will use customization functions and will be fully testable.Another solution may be to move to a full "effect" library pattern. However I can see the following effects, currently managed by
IO
, but that which will need to be detailed:req
(Exactly what I'm changing here)IO
mapConcurrently
readFile
putStrLn
.It will be a more important change, which can take the following shape:
KrankMonad
with all theses operations defined, with oneinstance
forReaderT KrankConfig IO
(i.e. the current implementation) and one for tests (something such asReaderT (Environment, KrankConfig) (WriterT Outputs (EitherT Exception Identity)))
.mtl
like implementation where each of the listed effect are implemented