Okay, with the playing field leveled, let's dive right in and essentially I've structured this talk into two separate parts. So the first part focuses on the general structure of tests, like how to organize everything. And then the second part is, you know, how to really write tests and, you know, two common pitfalls people fall into.
So, let's start with the first thing that I see happen a lot and also let me just point my test runner. I'm to that. And this is while I don't think there needs to be one assertion, only one assertion in a test, what I do think is that each test should cover exactly one use case. This is a mistake you can, you know super easy make.
So for instance here, the test description and really just focus on the test description here is, it should be possible to read from and write to the database. Now, and we see this test fails. If I'd asked, you know, like, what is it? Is it the reading part or the writing part? You probably couldn't just, you know, tell from the top of your head, right? And also if you're working, you know, on your software and this test, you know, starts going to read again, you also need to figure out, like, which part is it that you just broke, right?
Um, and my general rule of thumb here is that whenever you, you write the word end A and D, into a test description, just make it two tests, right? Um, you're combining too much in there already, um, which, you know, shouldn't be combined. So what can we do about this? Um, you know, we can split it up and let me just, you know, um, repoint my test runner again. So now we've got two tests. Once it should be possible to reach from the database. The other one is it should be possible to write to the database. Now, um, if the second one fails, we know, okay, it's definitely going to be the writing part. Um, that's broken. Not the reading part. That's nice because, you know, already from the, uh, from the test fail, we can see what's going on.
However, there's one little, small improvement left to be made here. So what happens, for instance, if both fail? Because here we can see that use cases aren't necessarily only, you know, like the keyword end isn't the only thing you need to look out for. You know, if you think about it, like what do both reading and writing to the database need a connection to the database? So if both fail, it could be that, you know, there are separate issues. It could just be that the connection isn't working. So this would be the last improvement I would make to this at a third test that just looks for the connection. So you know, if the connection test fails, and the reading, and the writing, okay, it's definitely going to be the connection. Definitely there is an issue there, and it's very likely that this affects also the reading and writing aspects, while this isn't as clear if you don't have that spec.
And some of you now might be thinking, you know, if you do TDD, this is something that would, you know, like this structure comes really naturally, because if you start building this interface, you definitely would start with ensuring that there is a connection to the database. So this would be the first test you write, and then you would add other tests after that fact. However, even if you do TDD, sometimes you get carried away, you know, if you want to get something done, and this is when you start cramming multiple use cases into one test, this will bite you in the ass down the line.
The second thing I'd like to talk about when it comes to the general test structure is the use of assertions, right? And the pitfall you can make or you can, you know, trapped into here is that essentially we can express everything as a Boolean expression, like and make it a true or false check, right? And I've started here, let me just skip that second test for now. I've started here by checking whether a certain property on a user has the correct name.