A ‘dry run’ is a testing process where the effects of a possible failure are intentionally mitigated. For example, an aerospace company may conduct a “dry run” test of a jet’s new pilot ejection seat while the jet is parked on the ground, rather than while it is in flight. Or, in software development, we can change the behavior of some methods in order to test, like avoid change data into a database (logging the action instead).
There are many ways to implement this capability. For example, we can add an explicity return in each method and test some condition. I will show some options in this article and we will find how to use the module MooseX::Role::DryRunnable in our Moose classes (Perl).
One simple example in Perl, reading from an environment variable
1 2 3 4 5 6 7
In this example, the
bar method change something in the database and my test is very simple, like a simple diff between log files. To test my application in dry run (to test in the product environment, for example, without a big risk), we can do this:
Sounds good, for large systems, with a good number of modules, it can be a problem. For example, the method
bar has two or three responsabilities: logging the parameters, doing the original job and do nothing if we are in the
dry run state. This method is doing a lot of things, and I have the same code in multiple places. Lets think about reuse of this code, using OO principles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Now it is interesting: my Foo class has just one job: dispatch (or not) the method call to FooBase (who knows our business rule). But we still have the problem of the same code in multiple places. Lets try to solve this with Aspect Oriented Programming, using Moose.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Moose is a complete object system for Perl 5. Consider any modern object-oriented language (which Perl 5 definitely isn’t). It provides keywords for attribute declaration, object construction, inheritance, and maybe more. In this example, we can use the Method Modifier
around and we can inject this new piece of code in one or more methods. But we still need add this hook in each class, this is why I create the MooseX::Role::DryRunnable.
Moose has a great number of features, like Roles. Roles have two primary purposes: as interfaces, and as a means of code reuse. In our example we can do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
In this example, the role DryRunnable provides the basic infrastructure to add the dry run capability. Instead ask direclty to an environment variable I’m asking to a method (
is_dry_run ), and instead only log / return nothing we call another method to do this (
on_dry_run ). Using this kind of pattern it is easy to inject the correct
around statement using, for example, MooseX::Role::Parameterized. With a parameterized role, we can set the list of methods in a Objected Oriented way, with more code reuse and less copy/paste. This is the base of MooseX::Role::DryRunnable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
The code of this role is simple, and we can set the list of the methods as a parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
I can extend the original role to provide a basic version of
on_dry_run for my set of Moose classes and write less code, overriding if I need something more specific for some class. And this is how we can deal with Aspect Oriented Programming in Perl, using Moose.
But this is not the only way to do this. I can use Monkey::Patch or Aspect to add the same behavior, there are many good options to do the same thing.
And there are many applications of this technique. If we identify a good reason to change the behavior of some class in runtime, like activate some modules or features (based on configuration, timedate, environment variables, etc), we can do something like this.