Build a Twitter Analytics App
6 Writing the Backend Twitter Server
9 Displaying our Data using the Flask Webserver
Testing, but especially Test Driven Development (TDD, where you write tests first) has generated a fair amount of controversy in the last few years. Even if you don’t agree with the detractors, it is worth knowing what the arguments are. Before you go ahead, please read these:
The main criticism is that TDD has become a religion, and as DHH says, it is used to shame and humiliate people:
Test-first fundamentalism is like abstinence-only sex ed: An unrealistic, ineffective morality campaign for self-loathing and shaming.
You may disagree with him, but there is something to his point that there is a lot of shaming going on, that does not help anyone.
Besides, tests first isn’t always possible. If you are building a library or API, or projects where you only have a rough idea what the final product will look like, writing tests first isn’t always possible.
TDD works great for some applications, not so for others. You just have to use your common sense, and not get sucked in by all the hype.
My advice is: When you are planning your project, add the time to test into the plan. It doesn’t matter when you test, as long as you have proper tests.
For this project, I will show you both how to write tests before and after. What’s important is that you write tests, not when you write them.
Behaviour Driven Development (BDD)
Some people say BDD is TDD done right, although the way I was taught, BDD was TDD.
What is BDD?
You figure out what behaviour your expect from your user, and then write tests for that.
What does that mean?
Let’s look at an example. Let’s look at the Twitter home page. You go to Twitter, you are not logged in, and you want to tweet something. What is the sequence of events the user will expect? And how can you test them?
Before we go ahead, take some time to think about this. How would you test Twitter? Don’t scroll down until you have written your own answers.
Okay, let’s have a look.
1 The user expects to be able to login if their username and password is correct.
Test: Try to login with correct username and password
Test: Check that wrong username or password results in failure
2 The user expects to be able to post a Tweet
Test: Logged in user can tweet. Unlogged in can’t.
Test User Interface: Test the Tweet length <160
Test Backend: Test the Tweet length <160
Why are we testing again? Because you can access Twitter in many ways, not just their website. It’s possible the front end to some app has a bug that doesn’t count characters. This is a security feature too: The backend must always repeat the checks.
3 The user expects to see their tweet in the live feed
Test: Can user see their own tweet?
Test: Can other users see this tweet?
Test: Is the tweet visible if using the streaming API?
Test: If the user used a hashtag, is the tweet visible in the hashtag list?
This is just a small list of tests, taken from one simple user behaviour (they want to post a tweet).
Most developers write test like this:
assert(add(1,2) == 3)
Each function has a test, no matter how tiny.
While this sort of testing has its uses, it can quickly turn tiresome. And not to mention, managers will think you are wasting your time.
While if you use BDD (which is actually just TDD done right), you can justify to your manager why you need these tests.
What not to test
There are some things you should not test. And yet every tutorial I see starts by testing them, and then pretends they have done “TDD”.
For example, many Django tutorials have a test where they can write to the database, and read back from it. So we are writing data to the database. Should we check the data was written correctly? Ie, we can read and write data?
In the same way, should we check if the Twitter api is working?
Those were trick questions. The answer is no. Because testing the database is the job of the people who wrote the database. Same for people who wrote the Python API to the database. And testing Twitter’s api is Twitter’s job. We only test our code.
We can assume, safely, that if we do a conn.commit(), the data will be written to the database. If it isn’t, raise a bug, but don’t go around testing databases.
So should we not test the database at all?
We need to test for things like, what happens when our script crashes without writing to the database. Will our data be lost?
Many of these things can be fixed with good design. So if you remember the database section, we are opening and closing the database in the main code, in a try-except-finally section.
conn = sqlite3.connect(db)
except Exception as e:
Even if the script crashes, Python will ensure that the database is safely closed.
Next: We will write our tests. As mentioned, the backend tests will be written after the code, while the frontend (Flask web server) will be written before the code, in pure TDD style.
Before we go ahead, can you think what the user expects from our app, and how you would test it?