ChancePy — Upgrade your Tests

Nadav Nuni
Kovrr Engineering

--

tl;dr

You can now use chancepy to generate random values of all kinds of sorts to aid your tests.

To install: just pip install chancepy

Why tests are important

Testing your code is important. It’s not always trivial and not always easy, but every experienced developer knows: pushing new code to a repo after seeing several hundreds of green tests is much easier than pushing to an untested repo. Moreover — seeing a test fail, and then having the ability to fix your code, increases the level of assurance that the code is fine, but, don’t get me started on TDD :)

Why randomizing in tests is good

Your tests should cover as many cases as possible, preferably, all of them. Let’s take a look at this piece of code:

emoji_dict = {‘apples’: ‘🍏’}def replace_with_emoji(sentence):   assert len(sentence.split(‘ ‘)) > 2   for key, value in emoji_dict:         sentence = sentence.replace(key, value)

Pretty simple to test. We could just use a 3 word sentence, right?

def test_replace():     assert replace_with_emoji(“I love apples”) == “I love 🍏”

Time passes and new emojis are added to the list, including the “love” emoji:

emoji_dict = {‘apples’: ‘🍏’, ‘love’: ‘💖’, ‘rocket’: ‘🚀’, …}

and suddenly your tests start to break. Of course, this would not be so hard to fix, but when dealing with high-scale systems, these issues can cause serious problems. This is an example of using constant “made-up” values and how they interfere with our testing efforts. Let’s look at another example:

def get_year_if_march(date)    if date.month == 3 and date.day != 30:    return date.year

As you can see, there is another “hidden” condition that is not described in the function name, it could have been added later, or just disregarded. Again, we could write a test like this:

def test_get_year_if_march(date):    date = datetime.date(year=2020, day=1 month=3)    assert get_year_if_march(date) == 2020

But then we’d miss the disregarded condition.

It would have been safer to write like this:

def test_get_year_if_march(date):    date = random.date(year=2020, month=3)    assert get_year_if_march(date) == 2020

The lesson here: When testing, using hard-coded-non-random values from the top of our head is bad practice.

Going back to our apples example, a correct test would look like this:

def test_replace():    test_sentence = f”{random.string()} {random.string()} apples”    test_words = test_sentence.split(‘ ‘)    assert replace_with_emoji(test_sentence) == f”{test_words[0]} {test_word[1]} 🍏”

A missing aspect in Python

Readers with sharp-eyes might see a usage of an unfamiliar method called random.string() . This method, of course, doesn’t exist in python. Same applies for almost any sophisticated content you might want to generate.

Javascript, on the other hand, has the amazing chance.js that contains all of these goodies.

How can chancepy help?

Introducing — chancepy a new open source library that brings the joy and sparkle of JS’s chance.js to python!

When you use it, you can write your test like this:

from chancepy import Chancedef test_replace():    test_sentence = f”{Chance.string()} {Chance.string()} apples”    test_words = test_sentence.split(‘ ‘)    assert replace_with_emoji(test_sentence) == f”{test_words[0]}      {test_word[1]} 🍏

Here at Kovrr, we use automated tests as a key part of our development process. We deal with complex objects that contain a variety of data types. We use chancepy to generate random values for these objects in our tests to make our tests more stable.

For example, an instance creation might look like this:

from chancepy import Chancedef create_random_attack_event():   return AttackEvent(        inception_date=Chance.date()        discovery_date=Chance.date(),        termination_date=Chance.date(),        malware_name=Chance.string(),        affected_records=Chance.natural(maxi=10_000_000)        damage=Chance.natural(mini=100_000, maxi=10_000_000_000),        effect_type=Chance.pickone([“deletion”, “exposure”])    )

Currently, chancepy has a lot of time related methods (random day, random timestamp, random timezone etc.) as well as general methods (float, string, natural etc.) and is under active development. Feel free to join the effort and contribute more awesome features!

--

--

Nadav Nuni
Kovrr Engineering

A Software Engineer at Kovrr. Likes Video and Board Games. Feel free to follow me on Medium for more engineering insights