inaka

Latest blog entries

/
Erlang and Elixir Factory Lite B.A.

A brief introduction about what was the Erlang Factory Conference in Buenos Aires for some Inaka team members

Jul 07 2017 : Euen Lopez

/
The Art of Writing a Blogpost

The Art of Writing a Blogpost

Apr 11 2017 : Matias Vera

/
SpellingCI: No more spelling mistakes in your markdown flies!

Feb 14 2017 : Felipe Ripoll

/
Fast reverse geocoding with offline-geocoder

Do you need a blazing fast reverse geocoder? Enter offline-geocoder!

Jan 18 2017 : Roberto Romero

/
Using Jayme to connect to the new MongooseIM REST services

MongooseIM has RESTful services!! Here I show how you can use them in an iOS application.

Dec 13 2016 : Sergio Abraham

/
20 Questions, or Maybe a Few More

20 Questions, or Maybe a Few More

Nov 16 2016 : Stephanie Goldner

/
The Power of Meeting People

Because conferences and meetups are not just about the technical stuff.

Nov 01 2016 : Pablo Villar

/
Finding the right partner for your app build

Sharing some light on how it is to partner with us.

Oct 27 2016 : Inaka

/
Just Play my Sound

How to easily play a sound in Android

Oct 25 2016 : Giaquinta Emiliano

/
Opening our Guidelines to the World

We're publishing our work guidelines for the world to see.

Oct 13 2016 : Brujo Benavides

/
Using NIFs: the easy way

Using niffy to simplify working with NIFs on Erlang

Oct 05 2016 : Hernan Rivas Acosta

/
Function Naming In Swift 3

How to write clear function signatures, yet expressive, while following Swift 3 API design guidelines.

Sep 16 2016 : Pablo Villar

/
Jenkins automated tests for Rails

How to automatically trigger rails tests with a Jenkins job

Sep 14 2016 : Demian Sciessere

/
Erlang REST Server Stack

A description of our usual stack for building REST servers in Erlang

Sep 06 2016 : Brujo Benavides

/
Replacing JSON when talking to Erlang

Using Erlang's External Term Format

Aug 17 2016 : Hernan Rivas Acosta

/
Gadget + Lewis = Android Lint CI

Integrating our Android linter with Github's pull requests

Aug 04 2016 : Fernando Ramirez and Euen Lopez

/
Passwordless login with phoenix

Introducing how to implement passwordless login with phoenix framework

Jul 27 2016 : Thiago Borges

/
Beam Olympics

Our newest game to test your Beam Skills

Jul 14 2016 : Brujo Benavides

/
Otec

Three Open Source Projects, one App

Jun 28 2016 : Andrés Gerace

/
CredoCI

Running credo checks for elixir code on your github pull requests

Jun 16 2016 : Alejandro Mataloni

/
See all Inaka's blog posts >>

/
TDD, Coverage and Why Testing Exceptions

A photo of Brujo Benavides wrote this on February 24, 2015 under erlang, exceptions, tdd .

At Inaka, we're always promoting the use of TDD. Especially in server applications and libraries. There is not just one way to work with TDD, but in this article I will focus on the way I usually apply TDD myself.

Whenever I'm faced with a new task I first write the tests that describe the expected system behaviour. Let's say we're adding a new endpoint to our API server (GET /nodes) that returns the list of current Erlang nodes where the server is running (I'm aware that such an endpoint is a security breach and should never exist, but let's forget about that for a second here). When faced with such an issue I would first write a test like this one:

get_nodes_returns_ok(Config) ->
  ct:comment(
    "API call returns 200 and a proper json"),
  Result = api_call(get, "/nodes"),

  ct:comment(
    "The list of nodes is the expected one"),
  CurrentNodes = lists:sort([node()|nodes()]),
  CurrentNodes =
    lists:sort(
      [list_to_atom(Node) || Node <- Result]),

  {comment, ""}.

Then, I would make sure it fails and finally add the code to make it run. Which would eventually boil down to something similar to the following lines (in the corresponding cowboy handler):

handle_get(Req, State) ->
  Nodes = net_adm:world(),
  Reply = jiffy:encode(Nodes),
  {Reply, Req, State}.

My test will then either always pass, or never pass depending on the presence of .hosts.erlang in either my current path or my home directory. If it's present, my test passes. Otherwise, the handler will always throw an exception and crash.

Let's assume that the presence of .hosts.erlang is an installation requirement. That is, we can be sure that if the appropriate installation procedure is followed when installing the server, that file will be placed in the expected location. Therefore, our test will never fail.

But we know there might be an exception thrown from net_adm:world(). And let's assume we already have a convenient utils:handle_exception/3 function that, given a request and a an error, logs it to the console and, without crashing, returns the appropriate status code for the request (maybe it's 500, but maybe it's 403, 404, 409 or other). So, it sounds reasonable to change our code in this way:

handle_get(Req, State) ->
  try net_adm:world() of
    Nodes ->
      Reply = jiffy:encode(Nodes),
      {Reply, Req, State}
  catch
    _:Error ->
      utils:handle_exception(Error, Req, State)
  end.

That handles it on the server side, but what about my test case? Should I simply make sure that I have a .hosts.erlang file when running the tests and therefore be done with it? Since it's an installation requirement, it must be there, right?

My usual next step is to check code coverage and aim to be as close to 100% as I can. In this case, the following piece of code will not be covered:

catch
    _:Error ->
      utils:handle_exception(Error, Req, State)
  end.

So, an intersting debate arises: Should I add the necessary test cases to cover that line as well or not?

One one hand, I'm pretty sure that line will never be executed on real servers. On the other hand, that line is there, right?

Let's say we don't test it, because it will never happen. The following questions appear:

  1. What if the function in the utils module is called handle_error or if it expects Req, Error, State and not Error, Req, State?
  2. What if utils:handle_exception in this case doesn't return a 500 error as expected and instead, for some reason, for this kind of error, returns a 400?
  3. What if utils:handle_exception for this particular error (or for all of them) causes the entire server to crash?

In any of those scenarios where the exception handling code is broken, what would happen if for some very unexpected reason (say, a misguided sysadmin deleting the .hosts.erlang file) it actually gets executed on a production server?

My usual approach here is to test the exception handling code, most likely by creating two tests from the original one and setting the environment so that one is run in a working scenario (in this case, with a .hosts.erlang file) and the other in a not working scenario (without .hosts.erlang). That way I make sure the exception handling code is covered and works as expected.

Or, there is another path, which is the answer to this question: If that thing is never going to happen, then why is that code there in the first place? If you can be 100% sure that you don't need that code, then remove it. That will also give you 100% coverage and usually make the overall code clearer.

Also, as Marcelo Gornstein once told me:

If you have less than 100% coverage, and you introduce a change later you'll have to wonder for each uncovered line if it's because your new tests are wrong or because it was never covered because it is not expected to happen. Adding a complete_coverage test case to your tests for those very unexpected lines of code [like code_change in gen_servers] will give you 100% coverage so that, later when you introduce something new, if you don't have 100% coverage again you'll be sure there is code you introduced that is not properly tested.