Any best practice around having different data in /priv for testing vs production?

88 views Asked by At

I'm writing tests with EUnit and some of the Units Under Test need to read a data file via file:consult/1. My tests make assumptions about the data that will be available in /priv, but the data will be different in production. What's the best way to accomplish this?

I'm a total newbie in Erlang and I have thought of a few solutions that feel a bit ugly to me. For example,

  • Put both files in /priv and use a macro (e.g., "-ifdef(EUNIT)") to determine which one to pass to file:consult/1. This seems too fragile/error-prone to me.
  • Get Rebar to copy the right file to /priv.

Also please feel free to point out if I'm trying to do something that is fundamentally wrong. That may well be the case.

Is there a better way to do this?

1

There are 1 answers

2
mpm On BEST ANSWER

I think both of your solutions would work. It is rather question of maintaining such tests, and both of those rely on some external setup (file existing, and having wright data).

For me easiest way to keep contents of such file local to given test is mocking, and making file:consult/1 return value you want.

7> meck:new(file, [unstick, passthrough]).
ok
8> meck:expect(file, consult, fun( _File ) -> {some, data} end).
ok
9> file:consult(any_file_at_all).
{some,data}

It will work, but there are two more things you could do.

First of all, you should not be testing file:consult/1 at all. It is part of standard library, and can assume it works all wright. Rather than doing that you should test functions that use data you read from this file; and of course pass to them some "in-test-created" data. It will give you some nice separation between data source, and parsing (acting on) it. And later it might be simpler to replace file:consult with call to external service, or something like that.

Other thing is that problem with testing something should be sign of bad smell for you. You might think a little about redesigning your system. I'm not saying that you have to, but such problems are good indicator to justify on . If you testing some functionality x, and you would like it to behave one way in production and other in tests (read one file or another), than maybe this behaviour should be injected to it. Or in other words, maybe file your function should read should be a parameter in this function. If you would like to still have some "default file to read" functionality in your production code, you could use something like this

function_with_file_consult(A, B, C) ->
   function_with_file_consult(A, B, C, "default_file.dat").


function_with_file_consult(A, B, C, File) ->
   [ ... actual function logic  ...  ] 

It will allow you to use shorter version in production, and longer just for your tests.