Finding and fixing bugs in your tests with fast-check
27th Jul 2020
But we didn't offer a fix to that initial testing bug.
We've been told that this is unsatisfying, so consider this post our redemption.
💻 The tests featured in this post are available in the corresponding GitHub repository.
If you're already familiar with the referenced issue, skip to the solution.
Imagine you have an input where users write in a price - but this input is
type="text" rather than
type="number". So you need to create a function (
getNumber) that converts the input string into a number and an accompanying test:
This passes 🎉
Now imagine your website also operates in Germany where the meaning of commas and decimals in numbers are switched (i.e. $1,200.99 in English would be $1.200,99 in German).
So you add another test case to address this:
But when you run the test, you hit an error:
Assuming that your website and input are equipped to handle various locales and localized strings, this would be a limitation of your tests - not a bug in your app.
There's something that can help though: Property-based testing.
First, we need to pick the property (or properties) to test for in this situation. There a couple of options:
- It works with strings that aren't formatted.
- It works with strings that are formatted using one of the accepted locales (in this case, that would be German).
- A combination of the two.
We'll tackle the latter: A combination of formatted and non-formatted strings.
Here's a peek at what our test will look like by the end of this post:
This is a lot all at once. So let's go through it step-by-step.
To begin, we need to set up our test:
Note: This test uses the
To test a combination of formatted and non-formatted strings, we need to pass two different arbitraries to
The first will be
float to generate the floating-point numbers:
The second is also a combination of sorts. To start, we need to write a
constantFrom function that takes in multiple, equally probable values. We'll use this for our German locale:
Then, we'll wrap
constantFrom in the
option function. With
option, the complete arbitrary will return either
null (a stand-in for the English default) or our values from
Prices in these locales only go to the second decimal place and we want to embody this in our test.
To do this, we'll take the
toFixed method on it. The
2 value indicates how many decimal places we want.
This method returns a string, but we need a number for our final assertion. So we'll wrap it in another built-in function,
parseFloat, and name this variable
Finally, we'll pass two arguments to the
testFloat (representing each generated floating-point number) and
locale (representing the generated locale from our
property function will look like this so far:
Because we're testing both formatted and non-formatted strings, we need a way to differentiate these two string types.
We'll create a variable called
floatString that checks if there is a
locale !== null, then we need to create a localized string based on the generated float.
Note: This constructor should mimic existing functionality in your app.
null, then we'll use the built-in
toString method on
fixedFloat, which returns it as a string.
At last, we need the most crucial part of any test: The assertion.
expect statement indicates that when you pass the
floatString (formatted or not) to the
getNumber function, you expect it to equal the
fixedFloat number value:
After all that, you're back to that final result:
Reminder: You can check out the code in our accompanying GitHub repository.
Now your tests are more thorough and resilient 🎉
Nicolas Dubien, the creator of fast-check, was the first to flag this issue in the original guide. He also helped develop the solution. You're the best, Nicolas!
Don’t miss the next post!
Absolutely no spam. Unsubscribe anytime.