In “Testing Behavior, not implementation details – part 1“, we’ve briefly touched on the term observable behavior. But what is observable behavior anyway? Explaining this as “whatever you see software doing” doesn’t help much. Especially not when we are talking about automated tests that focus on testing behavior. We need something more, something that we can use to write our valuable tests.

In this article, we’ll examine how to define observable behavior for our data stores and use that in our tests.

Observable behavior data store

As in the part 1, let’s use the example with tasks. Let’s say that users can store their tasks and list the open ones. To provide that, we can build a very simple task model and store:

The example is very straightforward. There are methods to add a task and list open tasks. Anyhow, how do we test that? A naive approach would be to write one test per method. Yet, in “Testing Behavior, not implementation details – part 1” we’ve seen that this doesn’t work. With such an approach, our tests were tightly coupled to the implementation details. Following the example from part 1, we would write a single test. Let’s try to specify our expectations better. We can write “users can store their tasks and list the open ones” as a set of following requirements:

  • The user’s open tasks must be listed
  • The user’s closed tasks must not be listed
  • Other users’ open tasks must not be listed

These are the three observable behaviors we interested in. We don’t care where and how the tasks are stored. Yet, we could observe that. Actually, that’s exactly what we’ve done that in part 1 when we were testing implementation details. But that’s not what brings value to our users. No user will ever say: “This TODO app is garbage because they’re using PostgreSQL. They would only be awesome if they’d be using MongoDB.”. On the other hand, I can clearly hear users complaining, “This TODO app is garbage. I’m seeing TODOs that I’ve never created. WTF?”. So that’s where we put our effort when writing tests. In our case, tests could look like this:

How do you spot observable behavior?

Above, we’ve examined a pretty simple example. In reality, things tend to get more complicated. So the question is, how do you navigate there? Here are the guidelines you can use to spot observable behavior faster and write better tests for your data stores.

Fetch multiple

Ask yourself – When should results be returned? When results shouldn’t be returned? Then, write a test for every condition case inside your query. When you have AND conditions, you can write one test for each case when AND excludes results + the case when results are returned. For more complex examples, you can draw a matrix of conditions where you specify when results should be returned and when not. Then, you can use the matrix to help you write tests for all cases.

Fetch single

Similar to fetching multiple results, you should ask yourself: When should the result be returned? What should happen when there’s no result? Then, write a test for every condition case inside your query. When no result is returned, test that either the correct exception is raised, or a default/empty record is returned, whichever makes more sense.

Storing

When storing objects, you’ll need at least one test—added objects can be fetched. You need to add more tests when you want to ensure uniqueness or some other constraints. For every constraint, you should add a test case. Then, you can assert that the correct exception is raised.

Conclusion

In this post, we’ve looked at how to define observable behavior and use that to write better tests. As you’ve seen, a number of methods doesn’t define how many and which tests do we need. This is specified by the “user” expectations. In the end, you’ve learned about guidelines that can help you be better at spotting observable behavior and writing tests for it on your projects.

You can find full code examples in this Github repository.

Subscribe To Python Testing Tips

Get Python Testing Tips to Your Inbox

Subscribe To Python Testing Tips

Get Python Testing Tips to Your Inbox

Share This Story, Choose Your Platform!