[erlang-questions] Testing gen_statem timers
Jesper Louis Andersen
jesper.louis.andersen@REDACTED
Fri Aug 16 13:06:21 CEST 2019
On Fri, Aug 16, 2019 at 12:36 PM Torben Hoffmann <torben.lehoff@REDACTED>
wrote:
> When testing I prefer to mock time so that I don't have to wait for the
> timeout to occur.
>
The key observation is this:
You want time to be an injected parameter.
First, note that there is no concept of "now". Any call to e.g.,
`erlang:monotonic_time()` is behind as soon as your code has that value, by
a small epsilon. In many cases, it is late by a lot; several milliseconds.
So it is better to have your code take a parameter which is a point in
time. When this is received, you roll time forward to that point in your
internal state.
Second, timers are the scourge of testing. They are side-effects which
means they are usually handled by mocking. And mocking is never the answer
if there is a decent way around it. However, if a timer turns into a
message, then we can choose to have timers handled outside the component
and then send messages into the component. This helps testing, as we can
run with a time-compression component, removing typical long waits in our
test cases. In fact, separation of effects from computation is almost
always a win for code in general.
Third, in systems you want a clock that is stationary and only moves when
you call `advance_time(Ms)` or something such. A clock which moves behind
your back is just going to create a lot of havoc in your code and
computation. So it is better to ignore the real physical world, and then
use messaging to handle the advance of the clock.
When I did some QuickCheck work, I built up a submodel which plays the role
as the internal erlang timers. This means the model can simulate timers in
the system and make choices as to when a timer fires, when time advances,
what the interesting timestamps are, and so on. In particular, the order in
which timers fire is nondeterministic. This is a mocking solution to the
problem, and it required the code to be structured around being easy to
hook. In QuickCheck tests, you must know in advance what is going to happen
to the system-under-test. Perhaps a way to ask a component for its current
timers, a way to disable the firing of timers and a way to inject a given
timer back would give a tester the tooling for doing dynamic interaction,
like most unit tests are doing.
Also, I've been toying a bit with Janes St. Incremental implementation (In
ocaml). They also have a stationary clock which can be advanced from the
outside[0].
[0] Getting self-adjusting computations into Erlang is actually a hard
problem. The implementation relies on being able to control the firing
schedule of incremental nodes, something you cannot easily do in Erlang
processes say.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20190816/3331e222/attachment.htm>
More information about the erlang-questions
mailing list