VueJS unit tests as a learning tool: v-if
This is the second post in a series which dives into unit tests within the VueJS code base in order to gain a deeper understanding of how elements within the Vue ecosystem work.
Beyond alerting one to regressions in a code base, well-written unit tests read like simple sentences and help those working on a project to understand what individual features do. Unit tests can also serve as great templates for documentation if they are written well as they should describe what a feature does and how it reacts to different inputs.
The VueJS documentation is excellent, but recently I decided to dig even deeper and begin exploring the source code behind Vue! The first piece of the code base I visited was the tests directory. Most of you have surely heard of Test Driven Development (TDD). Well, in this series I would like to introduce you to Test Driven Learning (TDL). The idea is to begin reading through the unit tests of the framework or library you are using early in order to gain a deeper understanding of how individual features work. TDL also has a beneficial side-effect in that it will reinforce the benefits of writing unit tests in your own projects and reveal solid testing patterns.
‘v-if’ unit tests
In this second post in the series we will take a look at the v-if
directive. The unit tests in full can be found here, but we will be walking through them piece-by-piece below. Below is the beginning of the v-if
test spec.
The first thing we notice is how similar v-if
is to v-show
. They both work on truthy and falsy values. However, the key difference jumps out at us right away as well. Whereas v-show
set the CSS property display: none
to hide an element, v-if
removes the element from the DOM and replaces it with a comment (<!—--->
).
This is a very important difference and gives us a big hint as to when we should choose v-if
over v-show
and vice-versa. If we do not expect the value bound to v-if
/v-show
to change often, if at all, v-if
is the best choice from an efficient rendering standpoint. However, if we expect this value to change fairly frequently (a toggle for example) then we should lean toward v-show
. v-show
performs better in these situations because its display
property is simply toggled rather than the entire block of HTML being destroyed and re-rendered. This information becomes clear as you compare the v-if
and v-show
unit tests before reading any documentation. Pretty cool!
Clearly the variable tied to v-if
could change throughout the lifecycle of a component. So what happens as v-if
evaluates to different values? The next set of tests explain this in detail!
The beginning of this set of tests reveals what we may have expected: as foo
is toggled from “truthy” to “falsy values ”the UI toggles from rendering the span
to removing it from the DOM. If you already checked out the v-show
post, this will all look familiar as it tests the same values.
The next set of tests introduces the v-else
directive and we see it added to the sibling of the span v-if
appears on. The tests are identical to the “truthy”/”falsy” tests we just saw above with one key difference. It appears that, thanks to the presence of v-else
, when the value bound to v-if
is “falsy”, the span
with the v-else
directive is rendered. Nice! So rather than adding a <span v-if=”!foo”>bye</span>
, we can simply utilize the v-else
directive!
The final block of this section of tests introduces another directive: v-else-if
. This directive is bound to a different variable, in this case bar
. As we can see through the tests: if foo
is “truthy” it is rendered, if foo
is “falsy” but bar
is “truthy” bar
is rendered, and if both are “falsy” the element containing v-else
is rendered. So these tests reveal that the behavior of v-if
can be expanded by using v-else-if
and v-else
. We can check multiple bound variables and the first one which evaluates to a “truthy” value will be rendered.
The next block of tests involve a directive we have yet to come across, v-for
. We will discuss this directive in detail in the next post in this series, but for now it is enough to know that it is used to iterate over values such as members of an array and render the element, in this case a span
, for each value. These tests simply reveal that v-if
works exactly how we would expect it to inside v-for
. “Truthy” values are rendered, “falsy” values are removed from the DOM.
The last couple tests show how v-if
works when coupled with v-for
. If the variable bound to v-if
is “falsy”, the element containing v-else
will be rendered for each “item” within the v-for
. In this case the digits “123” are rendered and then “321” is rendered after the “list” is reversed.
This next set of tests is interesting as it introduces a Vue component and tests how v-if
works on a component root. A new Vue instance is instantiated with a template containing a test
component. The “test” class is set on this component. This test
component is then defined to have an “ok” data property initially set to “true” and a template containing a div
with an id of “ok”, class of “inner”, and v-if
bound to the ok
data property.
We then test to ensure that the div
at the root of this test
component is indeed rendered by checking for the id of “ok” and both the “inner” and “test” classes. Next we flip that ok
data property to “false” and test again and find that the test
component has been removed from the DOM. Flipping ok
back to “true” renders our test
component once again. We now know how to combine v-if
with components to render or remove them from the DOM. Nice!
In this final test block we notice the mention of “unnecessary patches”. This should tip us off to the fact that we are about to learn about some efficiency optimizations of working with v-if
.
First, a couple spies are created through Jasmine’s createSpy
method. At a high-level what this is doing is simply “spying” on the created
and destroyed
methods so we can eventually test how many times they have been called (if at all). A new Vue instance is then spun up with an ok
data property set to “true”. A test
component is also created which “lives” on a sibling div
to the div
containing v-if
. The created
and destroyed
events are tied to this component.
We test that created
was called once as expected. We then flip ok
to “false” and test the number of created
calls once again. We find this is still one which means when the div
containing v-if
was destroyed the div
containing test
was not re-rendered. Finally, we check that this div
was not destroyed and find that it was not through the fact that destroyed
was never called. This is pretty awesome if you think about it. As the comment within the test states:
“when the first div is toggled, the second div should be reused instead of re-created/destroyed”
That’s a very efficient way to make DOM updates, ONLY update what has changed and re-use the DOM elements which remain the same.
‘v-if’ docs
The v-if API docs section should make complete sense to us now that we have studied the unit tests:
As I mentioned earlier, the VueJS docs are excellent, but I would argue that reading through the unit tests behind v-if
taught us all that the docs reveal and more in just a couple minutes of reading! Also, as I mentioned, reading through the tests taught us how unit tests should be written and how thorough they should be. Writing similar tests for features within your own apps should become a priority for you and your team!
Next up
In the next post, to be published soon, we will take a deep-dive into v-for
through its unit tests!
Previous posts
The first post in this series covered v-show
and can be found here.
If you enjoyed this post please hit the little 💚 below to let me know you’d like more of this material! Thanks! 🤓