Julio Biason
6 years ago
2 changed files with 148 additions and 0 deletions
@ -0,0 +1,56 @@ |
|||||||
|
+++ |
||||||
|
|
||||||
|
title = "On Unit Tests and Layers, Part II" |
||||||
|
date = 2017-09-15 |
||||||
|
|
||||||
|
category = "code" |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["unit tests", "en-au"] |
||||||
|
+++ |
||||||
|
|
||||||
|
After coming with a discussion about unit tests and layers, I got a |
||||||
|
bunch of other insights, specially from a video of Gary Bernhardt |
||||||
|
about "Fast Test, Slow Test". |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
Just after posting about |
||||||
|
[who one could see the layers through unit testing](./on-unittests-and-layers.md), |
||||||
|
I finally watched a video of Gary |
||||||
|
Bernhardt (of the "DestroyAllSoftware" fame) about "Fast Test, Slow Test": |
||||||
|
|
||||||
|
{{ youtube(id="RAxiiRPHS9k") }} |
||||||
|
|
||||||
|
Basically, what Gary is going after is saying "write tests for the layer ONLY, |
||||||
|
so all your tests are fast". Ok, I can get behind it, in a way. Because layers |
||||||
|
have their behaviour -- in a "mechanical" sense, since they don't require |
||||||
|
human interaction, but behaviour nonetheless -- so you're testing behaviour. |
||||||
|
|
||||||
|
But that also rises one question: *What are you testing?* Are you testing the |
||||||
|
*component* or the *application*? What do you deliver, anyway? *Components* or |
||||||
|
an *application*? How do you make sure you're delivering an application in the |
||||||
|
proper way? |
||||||
|
|
||||||
|
Testing layer behaviour also has a bad side effect: If you're application |
||||||
|
doesn't need a certain part of your layer -- say, you wrote a validator in the |
||||||
|
model layer, but the powers to be decided it wasn't required anymore --, how |
||||||
|
do you make sure it will go away? Your tests will still test those validators |
||||||
|
-- after all, you're testing your model layer -- and your coverage will still |
||||||
|
point that that piece of code is needed and you'll end up with a bunch of dead |
||||||
|
code that is kept alive only because the tests require them. |
||||||
|
|
||||||
|
Also, because Gary points out that the "integration tests" are still required, |
||||||
|
you'll end up with a lot more tests than necessary. Why not focus on the |
||||||
|
behaviour your *application* should have instead of the behaviour your |
||||||
|
*layers* have? |
||||||
|
|
||||||
|
I'm not against layer testing per-se, I'm just against writing tests that do |
||||||
|
not reflect the general expected behaviour of the application and duplicating |
||||||
|
tests because you're testing layer after layer and then testing them all |
||||||
|
together. You should test the *value* of your application, not its components. |
||||||
|
|
||||||
|
On a side note, Gary still does the same mistake everyone does, calling the |
||||||
|
"all layers" tests "system tests". That's wrong. Just because you're going |
||||||
|
through different layers it doesn't mean it can't be a unit test. It depends |
||||||
|
only on itself? Does it test behaviour? Congratulations, you have a unit test. |
@ -0,0 +1,92 @@ |
|||||||
|
+++ |
||||||
|
title = "On Unit Tests and Layers" |
||||||
|
date = 2017-09-11 |
||||||
|
|
||||||
|
category = "code" |
||||||
|
|
||||||
|
[taxonomies] |
||||||
|
tags = ["unit tests", "en-au", "testing", "layers"] |
||||||
|
+++ |
||||||
|
|
||||||
|
On a recent discussion about testing, I think I came up with a |
||||||
|
reason why some people really think we need to test everything: they |
||||||
|
are thinking in layers, without even realizing it. |
||||||
|
|
||||||
|
<!-- more --> |
||||||
|
|
||||||
|
This weekend I had an idea on why some people insist on writing tests for |
||||||
|
every single function and object, but first you must know that 3 things |
||||||
|
happened: |
||||||
|
|
||||||
|
First, I told someone a programmer-joke like this: "You can't teach top-down |
||||||
|
development to newcomers 'cause they don't know which side is up." (Actually, I |
||||||
|
heard from someone else; I'm not that smart to come with a line like this). |
||||||
|
Yes, it is a joke; yes, it is somewhat fun, but at the same time, it is a |
||||||
|
reality: we should let newcomers get wet before telling them how to properly |
||||||
|
design their project. |
||||||
|
|
||||||
|
The second is that I got "drafted" to do a tutorial on DjangoRestFramework and, |
||||||
|
because I was expecting someone to ask how to manage validation on forms and |
||||||
|
serializers and decided to learn a bit more about "fat models", which basically |
||||||
|
says you should put all your business rules on the model layer. And here is the |
||||||
|
part that stuck with me: the model is not an *object*, but a *layer*. |
||||||
|
|
||||||
|
{% note() %} |
||||||
|
"forms" are responsible for validating incoming HTML form content; |
||||||
|
"serializers" are responsible for validating incoming data from the API -- |
||||||
|
although "serializers" are also responsible for serializing the output data |
||||||
|
back to the client. |
||||||
|
{% end %} |
||||||
|
|
||||||
|
The third is that I was summoned to explain to a group to explain what should |
||||||
|
be tested. Thing is, they did a "fizzbuzz" code in which they had |
||||||
|
`multiple_of_3`, `multiple_of_5`, `multiple_of_15` and someone mentioned |
||||||
|
that we *should* test them too. |
||||||
|
|
||||||
|
And that's what got me thinking: Should we test it? My response for this |
||||||
|
question is always "no, think your project is a black box in which you press a |
||||||
|
button and some light goes on; you don't need to know if there is a nerve |
||||||
|
somewhere in your knee that you hit it and your leg does some kicking or if |
||||||
|
the signal is sent all the way to your brain and it sends a response back to |
||||||
|
the muscle to make it kick, all you need to know -- and test -- is that when |
||||||
|
you hit someone in the knee, they kick and *that's* your requirement and |
||||||
|
*that's* what you should test." But what if it is not that simple? |
||||||
|
|
||||||
|
The model *layer* has a responsability and has its own requirements: It should |
||||||
|
receive the data and store it somewhere; it can expose some business rules (as |
||||||
|
validation of said data) but shouldn't run those rules. The controller *layer* |
||||||
|
also has its own responsabilities and its own requireements: it gets the data |
||||||
|
from somewhere, do the checks, validate the business rules and then sends it |
||||||
|
to the model layer to be store. The view *layer*, again, has its own |
||||||
|
responsabilities and its own requirements: exposes the data to the user and |
||||||
|
receives data from the user and passes it to the controller layer, exposing |
||||||
|
the errors back to the user. |
||||||
|
|
||||||
|
In a way, all those layers could be separate projects: because they have a |
||||||
|
defined API, one could replace each layer independently -- I could have a |
||||||
|
model layer that stores all that in JSON and then replace it to one that |
||||||
|
stores in a Stream Processing Database and the controller and view layers will |
||||||
|
never know about. All those layers could be separate *libraries* -- a lost art |
||||||
|
of getting code with a single functionality and giving it its own space, with |
||||||
|
its own build tools and a well defined interface and which is its used by other |
||||||
|
projects as a black box: they know the interface, but they don't know how, |
||||||
|
internally, the library does whatever it does. |
||||||
|
|
||||||
|
And, because each library has its own code, it will have its own tests. |
||||||
|
|
||||||
|
And that's what people were seeing: They were seeing, unconciously, "this is |
||||||
|
another *layer* in the project, this layer should be tested because it can be |
||||||
|
replaced any time". Each `multilple_of` function was an *API* exposed from |
||||||
|
the model layer and, thus, should be tested. Even if the design doesn't seem |
||||||
|
to be layered and appear all around the code. |
||||||
|
|
||||||
|
Thing is, each layer is nothing by itself: There is no use for exposing |
||||||
|
business/validation rules without a controller to send the data; there is no |
||||||
|
use for something to validate incoming data and apply business rules if there |
||||||
|
is noone sending the data and noone to store it; there is no use for something |
||||||
|
responsibile for calling business rules check if there isn't business rules. |
||||||
|
All three layers are required to do the word -- and that's probably why I |
||||||
|
believe you should test them all as a single unity. |
||||||
|
|
||||||
|
But, in the very end, it seems that going full force into unit tests and what |
||||||
|
should be tested can actually help people see they layers of their projects. |
Loading…
Reference in new issue