sodapopcan

sodapopcan

Types 'n' Testing

After re-watching José’s keynote from last year, he brought up that he believes that reducing the number of tests you need to write is not something that typing brings to the table. This really hit home as it’s something I’ve suspected for a very long time now. For the eight months I was forced to use dialyzer at work, it changed absolutely nothing about how I wrote tests. Searching the internet has been of little help because it’s very easy to find a lot of accounts of people making this claim (sometimes claiming the test count reduction is significant) but almost always without example. The best I’ve found are tests whose sole purpose are to assert on the types.

Can anyone provide any insight here? If not it’s cool if this post slips, unanswered, into oblivion. If I get some examples of tests I wouldn’t write anyway, I have no plans to jump in and lecture why I wouldn’t write them. I’m more interested in uncovering a hole in my testing strategy. I’ve been working solo for over a year, now, (and previously at a company that wasn’t big on TDD) so I don’t have anyone to riff on this stuff with at work.

I also don’t want this turning into another debate on the merits of static typing. We did that already :sweat_smile:

Most Liked

stefanchrobot

stefanchrobot

From my experience as a solo dev, the most important tests are black-box integration-level tests that exercise the public contract of the app (UI if it’s a web app, the APIs if it’s a backend service). These tests are implementation-agnostic, so whether I’m using types or not has zero effect on them.

My app is a server-side rendered Phoenix app. I’m testing it by making multiple HTTP requests and asserting on the content of the responses. I have 173 of those tests plus 9 doctests and my suite takes ~6s to run. I threw away all other tests (no unit tests for schema or contexts). This gives me immense freedom in refactoring and rewriting the code.

12
Post #2
josevalim

josevalim

Creator of Elixir

My main point is that if your tests can be replaced by types, I would argue they are most likely tests not worth writing anyway. For example, I rarely see the purpose in checking for FunctionClausError (and similar).

On the flip side, believing types replaces tests (and docs), will lead you to lacking tests (and lacking docs). :slight_smile:

stefanchrobot

stefanchrobot

I don’t love complex type systems. My preference is for the types to fit the testing trophy, which to me means that the purpose of the types is to give the quickest feedback possible (right in the IDE) that something is off, before the code is even run. From that perspective, types supplement the developer’s experience.

lucaong

lucaong

My view is that testing and strong typing both help with lowering the cost of change, but they do so in different ways, and are therefore not a substitute of each other.

Strong typing pins down the basic interface of each callable function, defining what is expected as input and output. If a change breaks such expectation, the type system tells us. Additionally, when used properly, types also provide documentation, specifying explicitly what a function takes as input and what it returns as output.

Good tests pin down invariant properties of the system behavior, so that if a change breaks such invariants, the test tells us. Ideally, a test would assert an invariant property without restricting the specific implementation, so that one could change the implementation and still pass the tests, if the invariant is respected.

Here’s a toy example to illustrate what I mean. Suppose I want to implement a sort of key-value store, with two functions: set(key, value) that sets a key to a value, and get(key), that gets the value associated to a certain key, if any.

The type system can specify what keys and values are supposed to be, and even what happens if I try to get a key that is not associated to any value.

On the other hand, types won’t help in ensuring that if I set a certain key K to a certain value V, and then get the key K, I get exactly the value V and not another one of the same type. For that, we need a test like “set K to V. Then get K, and assert that the result is V”.

Such test asserts an invariant that the system should respect, without specifying what the implementation should be. Under the hood the system could be implemented as a hash map, as a database call, etc. without failing the test. This is easier said than done in more realistic cases, but that’s the aspiration.

Sometimes, a bug is discovered, and a test can be added to ensure that a similar bug cannot be re-introduced with future changes. That’s protecting against regressions, and again, ideally it would pin down some invariant that should hold for some corner case, rather than a specific implementation.

I don’t think that tests (or types) help much with ensuring correctness. What really matters for me is to lower the cost of change, and both tests and a good type system help, in complementary ways.

Nicd

Nicd

I guess it’s a problem of differing terminology but I don’t see what Gleam has to do with strong typing as it compiles to Erlang (strong typing) and JavaScript (weak typing), at least in the traditional meaning of the terms. But the type inference in Gleam is not complete and the compiler asks you to add types if it can’t deduce what you are doing.

If we look at some real Gleam code I wrote: src/glemplate/renderer.gleam · 5fae861c8649506405e88c5b79214979a20c8b85 · Mikko Ahlroth / Glemplate · GitLab

We can see that mostly the types can be left out in two cases: variable assignment and return values. These can usually be inferred by the compiler, but it’s still very much static typing, since static typing is not an alternative to type inference (Gleam has both).

If you want type inference that can guarantee that the compiler knows what types your variables / arguments / return values are, then it sounds to me that you want static typing with type inference, like Gleam or TypeScript do. I’m not sure type inference can meaningfully be done without static typing – we have surely seen with Erlang and Elixir that Dialyzer is not enough, and misses very obvious error cases (as it just doesn’t have enough information).

Where Next?

Popular in Discussions Top

chuck
Let me start by stating an assumption: Phoenix is a great approach to building REST APIs. There are many reasons for this, but I will ass...
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New
ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
AlexMcConnell
The reason that Rails is as popular as it is is because it’s very easy for relatively inexperienced developers to get a lot of work done....
588 19568 166
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
restack_oslo
Hello, Please pardon me for any faux paux. I am 46 and this is my first time on a forum of any kind. I wanted to to get answers from tho...
New
jsonify
So, is Heroku the only free option for hosting Phoenix/Elixir at this point? I’m not ready to commit to paying monthly and was wondering ...
New
tomekowal
Hey guys! I want to create a toy project that shows a chart of temperature over time and updates every 5 seconds. I feel LiveView is per...
New
joeerl
I’m playing with Elixir - It’s fun. I think @rvirding does give Elixir courses these days. Re: files and database - when I given Erlang ...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

Other popular topics Top

skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement