Using Variants to Simplify Widget Testing in Flutter
Hey guys, are you okay? Everything is fine with me!
Today I’m going to talk about a very cool subject, but very little commented on within the Flutter community. I’ll talk about using variants in your widget tests. By doing this, you will be able to have a much more concise test, avoiding repetition and boilerplate test code.
To get started I ask a question: have you ever needed to test your application on multiple platforms? If you’ve ever needed to do this and didn’t do it properly I can bet you ended up creating huge test files with an insane amount of code repetition. Did I get it right?
To solve this, Flutter allows us to create TestVariants. These test variants are nothing less than a description of a possible variation for your test. When creating this variation description, Flutter will be responsible for running its same test on all defined variations.
With that we can do only one test that runs several times, once for each variation definition. A practical example of this would be to run a widget test for each platform supported by the application, we’ll see how to do this later on.
Every widget test has a variant property, where you can just pass your variation. TestVariant is an abstract class, so we have to implement our own variant in order to use it. Once again Flutter makes our lives easier and gives us a simple yet powerful implementation of TestVariant. ValueVariant is this implementation that makes our life easier. Just pass our values and everything will work in our test.
Enough talking, and let’s get to the code.
Let’s say that for some reason we have a widget that takes an integer, meaning the size of a list to be rendered on the screen. We want to ensure that the widget will work properly regardless of the value passed to the widget. The widget code would look something like this:
Nothing too fancy here, I’m just showing n ListTiles, where n is the size received as a parameter.
I want to test that this widget behaves correctly when passed a size of 1, 5 or 10. The first solution that comes to mind is to create 3 tests, one for each of these cases. What’s that for if we can create a Variant for it? :-)
So let’s create our Size Variant, which I’ll kindly call the sizeVariant. His code is as follows:
Note that we create an integer ValueVariant, and to create a ValueVariant we need to pass a set to it. Here, it makes perfect sense to be a set as it wouldn’t make much sense to have repeated variation values. We create then, passing the desired values: 1, 5 and 10.
Once this is done, our test is almost ready to be written. The main motto of the test is as follows:
We still need to tell Flutter that we want to use our sizeVariant. We do this by passing our variant to the variant parameter.
That done, we now just need to capture the variant value within the test run. Now ValueVariant makes our life easier because it has a currentValue property, which just returns the value of the current variant.
We can then finish our test code:
When running this test, we observed that actually 3 tests were run, as Flutter ran our test once for each variant. Instead of coding 3 tests, we just made one and created a variant for it :).
The previous example might have seemed a little silly to you, but the power of variants can be used for more practical and useful examples. One of the best examples we can cite is how to use variants to test widgets on different platforms. In this way we can perform simple tests that test behaviors on all platforms supported by the application.
Let’s go to this example then.
Let’s say now that our example is as follows. I have a Widget that has adaptive behavior, that is, it behaves differently depending on the platform. We need to test this and we will do it with the help of variants as well.
See the example Widget below:
It shows the `AndroidWidget` widget if the platform is Android and `IosWidget` otherwise. Again, let’s resist the temptation to write a test for each case and let’s just write one test that looks at these behaviors.
Let’s start by creating our ValueVariant. It will now be a Variant of TargetPlatform. In our hypothetical example we want to test on two platforms: android and ios. So we pass these TargetPlatforms to our set.
Testing our adaptive widget becomes very simple then. We pass the variant platform to the ThemeData of our MaterialApp. The code is shown below:
Note that we need to make a conditional on our expects as they will have to change according to the variant being executed.
And the tests (one for each platform) pass :)
That’s it for today guys, see you next time. Happy Flutter Coding!