Using configuration to swap modules for mocks in your Elixir tests.

As you can see by this blog post I have attached a photo. If you were to look at the source for this image you will see its hosted on S3.

When it came time to test my Social module that I used for creating blog posts I had a challenge to figure out.

How can I prevent my image processor and uploader module that my Post’s modules uses for its image from really doing its job since I didn’t want to pay for uploading testing data and I didn’t want to slow down my tests let alone run my test in a CI that didn’t even have imagemagick?

The solution is as simple as just swapping out my processor and uploader module when running my tests with a mock module that has the same interface. But then how do I do that?

One solution is to use dependency injection, but I chose a slightly different approach to this problem. I decided that I would just use Elixir’s compile time configuration to specify what my processor and uploader module was. Then in modules that depend on the processor and uploader it would look to the applications configuration to get reference to the correct module to use.

Lets take a quick look at how I did that.

For my general configuration I would specify the normal uploader and processor module that way dev and prod or what ever other env would use the real modules like so.

config/config.exs

# General application configuration
config :polymorphic_productions,
  asset_uploader: PolymorphicProductions.Assets.Uploader,
  asset_processor: PolymorphicProductions.Assets.Processor

link to source

Then in my testing configuration I would specify to use my mock modules that had the same interface like so.

config/test.exs

config :polymorphic_productions,
  asset_uploader: PolymorphicProductions.Mocks.Uploader,
  asset_processor: PolymorphicProductions.Mocks.Processor

link to source

So thats all good and stuff, but how do I use the modules now that its been defined in my applications configuration?

Well lets look at our post.ex file that depends on that module.

lib/polymorphic_productions/social/post.ex

@processor Application.fetch_env!(:polymorphic_productions, :asset_processor)
@uploader Application.fetch_env!(:polymorphic_productions, :asset_uploader)

link to source

Using Application.fetch_env!/2 I’m able to set a module attribute that is a reference to my processor and uploader module and then use it as I normally would. Just like so.

%{path: large_image_path} = @processor.resize_image(image_path, "1920x1200>")
@uploader.upload(large_image_path, "/photos/large/", filename)

Now I don’t even have to setup my tests to inject my mock module I can just let the configuration do that hard work for me.

Now when I run my test I will use the mock module vs the real module that is used everywhere else.

And there you have it!

Some food for thought: If you want to make sure your mock and real module don’t diverge interfaces maybe try using elixir behaviors to insure the interface between the two modules.

Also you may want to look up alternatives to Application.fetch_env! to ensure that you don’t have a runtime issue as well as a compile time issue with your reference to your modules.

Happy coding!

c