This is something that will still make your tests flaky or non deterministic, because 10,000 milliseconds or 10 seconds might work in one case. And if it takes 11 seconds, it's not going to work anymore. And if it takes less than 10 seconds, then you are waiting more than you should.
There is also the reason of local versus continuous integration. When you are running tasks in your local computer, you have different computational power, computational resources than on CI and it might cause flakiness as well.
Component state. So for example, that you are trying to interact with a button that is not enabled yet or an input field that is not visible or there is some animation or transitioning happening. If you are Cypress user, the Cypress way to do it, for instance, for a button that is not enabled, you could before trying to interact with the button, do something like what I do in here, where you decide to get the button, ensure that it should be enabled and then you click on it.
And finally, another reason for tasks to become non deterministic is when you have dependency between them. So if you, for instance, try to put a dot only in the second one, since it depends on the first one to execute, it will fail. And we want to have tasks independent of each other because then we're going to have deterministic results.
And how can you find this non deterministic or flaky tasks? One way to do it is with test retries, but I want to say that you should use test retries as a way to identify the non deterministic tasks and not as a way to mask them.
There are different ways you can burn your tasks before making them part of the official task suite. A few of them, I had more options here, but I had to cut a few slides.
If you're a Cypress user, Cypress packs together with it, Lodash, which is a utility library which has many utility functions, and one of them is dot times. So you can wrap your it block, which is your task case, inside of Cypress dot Lodash dot times, and then you, as the first argument, you pass how many times you want this task to run. And the second argument is the callback function. Your task suite will be inside the callback function. For instance, if I want to be sure that my task is stable enough, I would run it three times, five times, ten times the time you think would be enough for you to ensure that it's stable.
Another way to do it, if you use Cypress as well, I'm a Cypress ambassador, so all the examples that I have here are Cypress related. Cypress maintains a library called grep, which allows you to add tags on your tasks like this one, where I added a tag called at burn, and then in the command line, you can run your task with cypress run dash dash env grep tags equal to the name of your tag. And then you can put comma burn and the number of times you want this task to execute.
This is something that I would love to see people doing on CI because what I mentioned in the very beginning, we don't want the task just to pass locally. We want to see them passing on CI as well. So if we burn them on CI, we can be sure that we are not introducing flaky tasks to our suite.
Very soon Cypress will be launching, probably in the beginning of next year, I think, a functionality called burn in, which will intelligently burn in your tasks if it notices that your task is new or it's a task that has just changed. So if it's new, it doesn't have any history. And because of that, you want to burn it before you make it part of your task suite. You don't want to just merge a new task if you don't know if it's deterministic, because if you do and you realize it's non-deterministic afterwards, it's already too late.
Our team will start having to investigate flaky tasks.
Comments