Wait! What is TDD?
So, you keep hearing about this TDD thing and want to start using it but have no idea why. Don't worry, the internet to the rescue! But first, what is TDD?
TDD stands for Test Driven Development and is a way of writing code. It means that you first write your test, you see it fail, and then you write the code that makes it pass. This is also called the red-green approach. Long story short, it's Development Driven by Tests.
Small example: Let's say you want to write a method that adds 2 numbers. Now, instead of implementing the method, you write the test first:
Now, you run the test, and you see it fail:
Only now, you know what the problem is, so you can implement the method:
And now, the test passes:
Now, that’s quite satisfying, right?
So, this does a few things:
- Makes sure your code is tested, so you don’t have to spend the last 2 days of the sprint writing tests for all the code that you wrote( which never happens )
- Makes sure you write code in a pragmatic way. There is a thing called YAGNI(You aren’t going to need it) which is about developers over-thinking and trying to solve climate change and world hunger with the next bit of code. Safe to say, most often than not, this makes the code overcomplicated without any real benefit.
- When you finished writing a feature this way, you saw all the tests pass, that gives you confidence that if/when you refactor if the tests are still passing, then you didn’t break anything. How often were you afraid of breaking the product with your simple Pull Request?
- Last but not least, it’s all about the colors! Seeing colors makes us happy. Seeing something failing and then passing, makes us even happier.
3 months and 500 tests later, we have a real sense of confidence over the product that we are building.
The keyword here is: CONFIDENCE
Now, back to Angular and our component
So, now you have an idea about what TDD is. Next stop, using it in our component. For this post, I am assuming you have a basic understanding of Angular, so I won’t go into details on how it works. I’ll be using Jest for my tests but the principles are the same.
For our example, let use a fictitious app that lets us create lists with movies. Now, somewhere in space and time, we decide that we also want a page where a user can see their favorite movies. So, without further ado, let’s build it!
First stop, let’s generate the component: ng g c favorite-movies
Now, this will generate a component. So, let’s see, what do we want to do first? We want to add a title like Favorite movies. Let’s do it!
WAAAAIT! What did we discuss? Write tests, then write the code that fixes it.
So, what would that look like? Something like this:
We created a new describe block for Rendering and there we added a new test that checks that there is an h1 tag and that it has the correct content. Now, we run the test and, big surprise, it fails! Don’t fret, we can fix it:
We added this to our HTML template and now our test passes!
Congratulations! You wrote your first bit of code in a TDD way. Take some time to pat yourself on the back. Good job! Now stop it. We have more work to do.
Next, we want to test that, given a list of movies, they are shown in the HTML. So, what do we do? We write a test for it!
Here we added a new @Input() property to the component, we created a new list of movies, and we passed those as input to the component. Then, we are testing that the rendered HTML contains the correct amount of elements with the class movie and that all the movie titles are displayed.
Of course, this test fails. So, let’s make it pass:Of course, this test fails. So, let’s make it pass:
This is what we add to our template. Yei! Tests pass now!
Neeeeext!
What if our data actually comes from a service asynchronously? Let’s adjust our test for that:
Now, our tests assume a service is injected. What we do is mock the response and check that the service is called. Tests fail, let’s fix them!
Now our component instead of having an @Input(), uses a service. The last puzzle piece is the template:
So, now we have a fully working component that gets data from a service and renders it. What else do we need? Nothing! Time to go home and play some Red Dead Redemption 2
WAAAAAIT! What if there are errors? Ok, ok, let write a few tests to make sure that in case something fails, it is properly handled:
So, we want to make sure that the error is displayed if getting the favorite movies fails, and it is hidden if everything goes according to plan.
What we need is to catch the error in the component:
And to show the error in the template:
Now, after all our hard work we have a fully working component that uses an async service to get data, handles errors and renders everything that we expect it to.
Now, time for some eye candy:
Pretty cool right?
Now, in order for this to work, you don’t need to implement the service. It just needs to exist and have a method that is called getFavoriteMovies.
Wrap-up
I know, I know, lots of words. I tried to be as succinct as I could without omitting any important information. Easier said than done. This is my first Medium article, so, I’m still learning. Hopefully, in the next one, we’ll talk about testing an Angular Service. TDD of course.
Hope you have enjoyed this program 📺. See you in the next one!
Be kind to each other! 🧡
Over and out
Test Driven Development in an Angular World— Part 1