Hacker News new | ask | show | jobs
DRYing Elixir Tests with Macros (hugoribeira.com)
92 points by hugoribeira 3781 days ago
8 comments

This observation is orthogonal to the original article but: often DRYing tests is a bad idea. Two principal reasons: 1) if you need a lot of boilerplate, you might need to look at refactoring your classes to reduce dependencies 2) non-DRY tests are often easier to read due to increased indirection.

None of which means that I don't reserve the right to DRY tests out when I think it's contextually appropriate.

I agree, you should be very careful about DRYing tests. Making sure and endpoint touches the authentication layer seems like a very good case to break that rule of thumb.

I've been writing Web Applications for a while now. There was only a handful of cases where I needed to repeat the same test case over and over again (authentication being one of them).

If it happens too often, it's a good tell that you're missing a responsability delegation into a concrete module/class somewhere.

This is a very cool demo of use of macros.

On testing, I think DRY principles are a good thing in your production code but I don't believe there is anything wrong with having lots of repetition in your test code. Each test can be read from front to back by the reader. If you have to wander around trying to understand the design of the test then that may not be ideal. Shouldn't need unit testing for your unit tests :P

I'm always very careful when placing code supporting the test outside of the test itself. Setup and teardown functions are fine, but I prefer to be safe because, well, you can't test the test.

-- I'm not saying this is the case in the blog post. Just some thoughts.

If a macro used for a test suite is substantial enough and used often I'd say it's worth testing
True. It's an Elixir best practice to keep macro definitions short and basically make them invoke functions as match as possible.

No perfect answer I guess...

I think this would be done better as a private function that takes a conn, a method, an action and an expected response code.

No need to complexify things unless you absolutely must.

Agreed. Elixir's first rule of macros is to not use macros. Generally idiomatic elixir is to try to encapsulate your problem in (ordered by priority) a 1) data structure 2) function 3) macro. So if you can express your problem in something other than a macro, do so.
Erlang/elixir is getting some awesome love from hacker news. <3

This is pretty sweet for testing repetitive tasks and also to get starting with macros on elixir.

Erlang has always done well on HN. One time the whole front page was Erlang submissions after pg asked for more Erlang.
All the good bits are there, I hope it gains sustainable traction :)
Was it not possible to simply write a function to achieve the same result?
I don't think so, I believe the function calls need to be there at compile time so that the test actually gets run.

But I'll give it a try tonight though :)

I don't think you can get the exact same syntax without using a macro. But couldn't you have just your entire assertion in a function and pass in the conn, method, route name, and expected json status?

Then you just wrap that in your test "..." form.

I don't see much gains from writing a macro, I see more drawbacks.

Pros of just using function and wrapping it in test form:

* It's just a function, easier maintenance. If you write a macro, you have to debug not only the macro but the code your macro writes.

* You gain more clarity into what each test is testing when there are failures. You hard code the "requires authentication" part as your test string. To write a macro that is flexible enough to also allow the user to specify the string here would end up defeating all purpose of it.

* You unquote the three arguments you pass in to the macro immediately. That's a red flag to me that this shouldn't need to be a macro. It's just to avoid the test boilerplate.

Cons:

* It's syntactically longer

I'm coming from a Clojure perspective, I've dabbled briefly with Elixir so let me know if I'm wrong. I wanted to get into Elixir more but the community's pervasive use of macros is keeping me from embracing it. They say Elixir is less magical and more explicit than Ruby and while that's not an untrue statement, I'm seeing too much metaprogramming already with macros. I'm not saying all macros are bad, it's just the vast majority of them are unneeded.

If you look at the code, that's actually what the author has done.

They've made a `make_unauthenticated_request` function that does the work, and the macro just calls it.

But, I agree, I'd probably have skipped the macro. And DRYed it up with just the function.

Right. That's exactly what I'm trying to point out. The point of the macro is to get the test form stuff.
ExUnit.Case.test/3 is itself a macro.
I've been thinking of exactly the same thing over the last couple of days and controller tests are prime candidates for this kind of DRY-ing, e.g. one would want to make sure resources owned by a user are actually scoped to that user and unaccessible to others. That is something that needs to be done on every action. Perfect case for DRY-ing up.
I like tests to be as unclever as possible, I want to know my tests are actually testing the code and not passing incorrectly.