Testing is often an overlooked part of app development. Unit tests are the most trivial testing software you can implement in your project anytime. You only need the flutter_test
package in your dev dependencies (it's added to every project by default).
Unit tests help to maintain your code, discover bugs quicker and ensure the quality of your code. They're also a key factor when working with legacy code.
How to write unit tests
Let's say you have a simple method that calculates 6 * 7:
The test for it would look somehow like this:
Pretty simple, but in the real world, you'd have a class that calls some other method in its dependency and returns some data. More or less something like this:
First, we need to create an instance of our class, but we have one problem since it relies on NetworkManager
. We could also create a NetworkManager
class, but that's not a point of unit tests. When unit testing, you should always limit what you're testing to the very minimum, so you know what failed precisely.
With the help come mocks, which allow you to create simulated objects that imitate the behavior of real objects in controlled ways. You'll need to add mocktail
to your packages. Then, create a mock class that you want to imitate, like the one below:
Now you can use it in your tests:
Let me explain what happens here. First, you create a mocked version of NetworkManager
, then pass it to the class you want to test. Subsequently, you tell the NetworkManager
what he needs to respond with when calling the getData
method. Finally, you execute the function and compare the data.
There's also one cool thing that you can do with mocks. You can verify if the methods inside of them were called or not. It comes in handy once your method doesn't return anything (void method).
Here we're verifying that the getData
method was called once and that there were no more calls on our mockNetworkManager
.
Working with unit tests
Unit tests are a great way to keep your project together. Let's say you change one of the methods to do something else. For example, the calculate
method now calculates different equations 3 * 7
- the tests won't pass now, and you will need to update them.
Why is this useful? This example asks you to double-check your changes, but let's see what happens if something changes in the getParsedData
method. There are way more scenarios that can go wrong here. For example, you could call getData
twice - the tests will fail.
You could also call another method from NetworkManager instead of using getData and the tests will also fail. You're notified about the changes and assured the changes are made for a reason.
Useful methods
emitsInOrder
- helpful in testing streams or blocs
expectLater
- works likeexpect
, but completes when a matcher has finished matchingpumpEventQueue
- waits for an event loop to run 20 times (default), helpful when waiting for some async work to complete