Isolating a Functional Core
Learn how to isolate the logic out of a GenServer's module for testing.
We'll cover the following...
The example covered in the previous two lessons is basic, but the more complex the internal logic, the harder it is to understand the tests. It becomes very tempting to start reaching into the GenServer for its state more and more.
A more straightforward solution would be to move the logic out of the GenServer’s module and into a new, purely functional module. This allows us to take advantage of the ease of testing purely functional code while leaving the GenServer tests focused on what GenServer does best: maintaining state and presenting an API.
Purely functional module
First, let’s look at the tests and code for the purely functional module, which we’ll call the RollingAverageMeasurements
because it’ll now handle most of our code logic. This module is concise and clearly scoped:
#file path -> example/lib/measurements.ex#add this code at the indicated place mentioned in comments of example/lib/#measurements.ex in the playground widgetdef new(max_measurements) do{[], max_measurements}enddef add_element({measurements, max_measurements}, new_element)when length(measurements) < max_measurements do{[new_element | measurements], max_measurements}enddef add_element({measurements, max_measurements}, new_element) dowithout_oldest = Enum.drop(measurements, -1){[new_element | without_oldest], max_measurements}enddef average(measurements) doEnum.sum(measurements) / length(measurements)end
The RollingAverageMeasurements
module now contains all of the pure data ...