Test Driven Development(TDD), is a discipline in software development that prohibits us from writing our code/implementation before writing the test first.
According to Uncle Bob, there are 3 rules for TDD:
1. You are not allowed to write any production code unless it is to make a failing unit test pass.
2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
_3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
It is a cycle we have to iterate over. We write a test on something that we want to implement such that when the test is run, the result will be “fail” then we write an implementation that is just enough so that the test pass. We then fix our code quality which we may have overlooked since we were focusing on making the test pass without making the test fail.
Some try to write the implementation first, the test later and then committing only the test. This is now how one should go about TDD. You should note that you are only allowed to implement just enough code so that your test pass(rule 3 according to Uncle Bob).
TDD In Flutter
To understand this concept in Flutter, let’s jump into a practical example. For the purpose of this tutorial, I will be using the PiggyX application. The codes are available on Github. Click here to access the codes.
We would create and test a couple of simple functionalities: EmailFieldValidator , PasswordFieldValidator . Let’s separate the login logic by creating a new file call loginpage.dart and implement as such:
class EmailFieldValidator {
static String _validate_(String value) {
return false;
}
}
class PasswordFieldValidator {
static String _validate_(String value) {
return false;
}
}
Let’s write tests for the above validators. We head over to the test folder and delete the defaultwidget_tests.dart and add a new file called fieldvalidators_tests.dart . We implement the test as shown below:
import 'package:PiggyX/ui/loginpage.dart';
import 'package:flutter\_test/flutter\_test.dart';
void main() {
test('empty email returns error string', () {
final result = EmailFieldValidator._validate_('');
expect(result, 'Email can\'t be empty');
});
test('non-empty email returns null', () {
final result = EmailFieldValidator._validate_('email');
expect(result, null);
});
test('empty password returns error string', () {
final result = PasswordFieldValidator._validate_('');
expect(result, 'Password can\'t be empty');
});
test('non-empty password returns null', () {
final result = PasswordFieldValidator._validate_('password');
expect(result, null);
});
}
In the above, we expect the following cases:
i) The email field must not be empty
ii) The password field must not be empty
We can extend the test cases if we want to but for the purpose of this tutorial and this app, we would be sticking with these two.
When we try running flutter test before the implementation and you might see +0 -4: Some tests failed.This tells us that 0 test passes (+) and 4 tests have failed (-)😪 😪 😪.
Let’s fix them now in our TDD mode!
Head over to loginpage.dart and fix EmailFieldValidator with:
static String _validate_(String value) {
return value.isEmpty ? 'Email can\'t be empty' : null;
}
Run the test again and you should see +2 -2 .
Now let’s head over to loginpage.dart and fix PasswordFieldValidator with:
static String _validate_(String value) {
return value.isEmpty ? 'Password can\'t be empty' : null;
}
Running the tests again, you should +4: All Tests Passed! 💃 💃 💃
You have successfully tested your app in a TDD manner with Flutter! You deserve some accolades. Now, it’s safe to complete our cycle since the objective of our feature is now completed (only making the UI)!
The codes are available in this GitHub repo PiggyX.
Bonus Points
For mocking dependencies, you can make use of the mockito package. For example, we need to be able to mock data from an app needs in order to authenticate against an API instead of relying on the live API, emulate a live web service or database to return specific results depending on the situation, operations with firebase.e.t.c. All we need to do is to create an alternate implementation of a class by either hand-coding or using the mockito package. You could follow this guide on mocking.
If you have any questions, feel free to leave a comment 🙂.
References:
Flutter Testing
Testing Flutter Apps
Mocking Dependencies Using Mockito
Mockito Package
Top comments (0)