Shortening Flutter test feedback cycle in VS Code

Oleksandr Leushchenko
Flutter Community
Published in
5 min readOct 21, 2022

Hey, my name is Oleksandr. I’m a GDE in Flutter & Dart from Ukraine. This article describes a simple trick that will help you to write quality apps more effectively when using Visual Studio Code.

TL;DR

  1. Install the “Coverage Gutters” plugin and make it watch
  2. Run spec --coverage --watch .

Test-First Approach

Having automated tests is critical for any mobile application. Tests not only help to find issues before real users will, but they can also help during development. It’s much harder to mess with the app’s architecture and write coupled classes when you cover them with tests. Hence, just by writing tests, you get more maintainable and refactoring-friendly code.

How to check that you have enough tests? You may look at the coverage report and see what lines of code are covered by tests, but sometimes that gives false positives. You may write completely useless tests that cover the code but validate nothing. That happens when you write tests that check the code you just wrote instead of the functionality the code should provide. You may argue, “why would I write code that does not do what I want?” but this happens all the time as this is the root source of most bugs.

The simplest way to write meaningful tests is to write them before you write code! You may focus on requirements first and only care about implementation details. Probably the most popular test-first technique is called TDD (Test-Driven Development). It splits the development process into three stages:

  1. You write a failing test. This test will cover some minimal pieces of functionality. It’s not supposed to be a product increment, just a test that validates a tiny bit of code like the “if” condition, variable initialization, etc. After you write this test, it is crucial to run it and ensure it is failing. Believe me, one day, the test that is supposed to fail, will pass, and you will be highly puzzled. 🙂
  2. You write the code that makes the test pass. You should not care about code quality, maintainability, or any best practice at this stage. Your job is to make the test from the first stage pass.
  3. Now you refactor and make the code shine by taking care of architecture, decoupling, DRY, and all other good things. You may refactor with confidence as your code is covered by tests that check not the implementation but the purpose of the code.

Coverage report

When using TDD, the chances of writing uncovered code are low but never zero. Moreover, it could be that some code you wrote is no longer needed after the refactoring and can be removed. But how to find it? For that, generate a test coverage report by running the following command in the root folder of your app:

flutter test --coverage

The report is generated to a lcov.info file. It is not human-readable. One way to make it useful is to use the genthml utility (it’s a part of lcov) that will generate an HTML report:

genhtml coverage/lcov.info -o coverage_report

The annoying part here is that you will need to constantly regenerate this report and continuously switch between browser, terminal, and IDE. To make it easier, you may install the “Coverage Gutters” plugin by Ryan Luker. This plugin highlights all lines covered with tests with green and all uncovered lines with red. To make the plugin constantly observe lcov.info files, press the Watch button that will appear at the bottom of your IDE:

That’s what you’ll see when you click the “Watch” button

Shorten feedback cycle

When using TDD, you will find yourself constantly running tests. During the first step, you run it at least once (to double-check that it is failing). Then, when you work on the code, you run tests repeatedly to ensure that the implementation fulfills the requirement, and during the third stage, you run tests to check that you haven’t broken anything during the refactoring. When using the “Coverage Gutters” plugin, you don’t need to generate the HTML report all the time, but still, you need to run the command that produces the report. Is there anything you can do?

Absolutely! You may install spec — a CLI tool from Invertase that can automatically run tests each time you save a file! The magic works with the following command:

spec --coverage --watch .

This command will run spec that will generate a coverage report and stay in “watch” mode, looking for any changes in the code or tests. The moment you save modified files, it will regenerate the report.

Now you don’t need to switch between different windows, and you can focus on what is essential — requirements, tests, and the code. You don’t need to leave your IDE as spec generates the report, and “Coverage Gutters” visualizes it.

What can go wrong?

Unit tests are usually blazingly fast. Unfortunately, not when you have lots of them and generate a coverage report. The more tests you add, the more time it will take before you see the accurate picture in IDE.

I recommend two solutions:

  1. Care of your tests the same way you care about code. Tests require refactoring and optimization. Sometimes you may find duplicates in your tests, so some tests can be merged or even deleted.
  2. Split the functionality into separate packages. Many packages will little code/tests are easier to maintain than one package with everything. If you haven’t heard about micro-frontends, you should watch the “Building Flutter apps in Lego style” talk on Flutter Vikings that I was giving with my beautiful wife Anna Leushchenko.

Instead of conclusion

This article was written by a developer from Ukraine. russia has invaded Ukraine and already killed tens of thousands of civilians, with many more raped or tortured. The death toll keeps climbing. It’s genocide. We need your help. Let’s fight back against the russian regime. Help Ukraine now:

--

--