Skip to main content

Distribute your nose tests across multiple machines, hassle-free

Project description

# distributed-nose Distribute your tests across multiple nodes with no hassle

## TLDR

### Before

Machine 1:

$ nosetests long_test_suite … Ran 1312 tests in 401.109s

Machine 2:

$ echo “I’m bored :(” I’m bored :( $ uptime | awk -F’load average:’ ‘{ print “Load: “ $2 }’ Load: 0.00, 0.00, 0.00

Developer:

$ google-chrome http://news.ycombinator.com

### After

Machine 1:

$ export NOSE_NODES=2; $ export NOSE_NODE_NUMBER=1; $ nosetests long_test_suite … Ran 660 tests in 220.502s

Machine 2:

$ echo “I feel loved” I feel loved $ export NOSE_NODES=2; $ export NOSE_NODE_NUMBER=2; $ nosetests long_test_suite … Ran 652 tests in 214.007s

## The Saga of Your Test Suite

### Oppression of Untested Code

The story of Your Test Suite has its heroes and its villains, its triumphs and its missteps. Like many other stories, it begins with a solitary hero. Graduating from university, she joins on with a merry band of software engineering adventurers. Led by management and supported by a wide cast of characters, they built things that the common folk loved and needed.

Slowly slowly, things begin to change. Poorly-structured and strongly-coupled code sneaks in to the code base, making changes increasingly difficult and allowing devious bugs to emerge. Party members begin to regularly break disparate parts of the project, the realm being too large to survey quickly for one person. Areas of code become off-limits, as dragons and unknown things claim all who attempt entry.

Through all this, our hero watches with a heavy heart. What is to be done about this untested code-base? Surely she can’t change everything by herself? She is just one person!

Finally, the daily struggles wear her down.

“Enough is enough!”

She resolves, pontificating on how to best strike against the oppression of untested code.

“I’ll write tests, and commit them to source control!”

She says, understanding that working code is the best possible argument.

### A Nose-fueled Peace

Methodically wielding and teaching [Nose](https://github.com/nose-devs/nose), she recruits from within to form a merry band of like-minded developers. They are the fellowship of TDD and they are determined to succeed. Tests are committed in flurries of high-value code!

Tales of the fellowship’s feats stir excitement in other developers. Another hero decides that if running some tests is good, then running all the tests all the time is better! Brandishing his hat of System Administration, he utilizes extra server resources and a Continuous Integration process is born.

Our heroes grow over-confident in their success, however. Making little attempt to justify the practice, word of time spent writing tests gets to management. Managerial Oversight moves to squash our fledgling fellowship.

### Managerial Push-back

“If you have time to write tests, I must have not given you enough to do!”

They snarl, threatening with the most dangerous of weapons: The sling of passive-aggressive disagreement.

Once again, our heroes summon their courage, and through great effort, boldly vanquish Managerial Opposition with the sword of high quality. A new era of peace and productivity begins and The Project flourishes.

But that peace was not to last. The specter of Slow Test Runs looms over our heroes. Soon, test failures go unnoticed. Developers aren’t waiting for tests to finish! The Test Suite falls in to decay.

### Multiprocess Arrives

Developers grumble and management freely lobs I-Told-You-Sos. The darkness of the age before tests threatens to return.

“I’ll speed up our tests!”

A voice cries out. It’s our hero again, vowing to strike at the root cause of decay.

Pouring over ancient texts, she quickly discovers [Multiprocess](http://nose.readthedocs.org/en/latest/plugins/multiprocess.html).

“This will release us from our single-core shackles.”

She proclaims, confident. Hours later, her 8-core machine races through tests with alacrity. Soon, multiprocess is in use on the CI server, and test speed races forward.

All is right in test land! Her fellow developers throw a break-room party in her honor. Python Meetup talks flow like water and TDD is once again well-liked by all.

Like the previous peace, this too is temporary, as adding more cores is costly. Fast forward several score tests, and the once-speedy test suite regularly brings the beefy, multi-core CI server to its knees.

### Distributed Despair

Remembering the ease of implementing Multiprocess, our hero knows the drill. She’ll simply distribute the tests across multiple servers! The same ancient tomes are confidently consulted, as the solution seemingly obvious.

But wait!

Where are the simple guides?

Our hero isn’t a wizard. She can’t decipher the ancient scrolls of distributed systems. She doesn’t have time for formal wizard training, and the number of moving parts involved is boggling.

Despair sets in as the once-speedy test sweet becomes slower and slower. The outlook for our adventurers is bleak, as an evaluation of speeding up the tests themselves reveals immense challenge. Some even suggest abandoning the test suite entirely with a new focus on speed!

Factions form and the party threatens to splinter entirely!

### Distributed-Nose: The Scalable Solution

“Wait a minute.”

A clear voice breaks through. It’s our hero, once again.

“Why do our test runners need to communicate to coordinate? Our memcached servers need the same level of coordination and achieve it with ease!”

Tickled by the flickering of an idea, she sets to work. She is steadfast, pouring over tomes on [Consistent Hashing](http://en.wikipedia.org/wiki/Consistent_hashing). Once the solution is clear in her mind, she happens upon a fellow adventurer with sound advice.

“Have you tried distributed-nose?”

“Unfortunately. I’m terribly allergic to pollen and I get that every spring.”

After the initial confusion is resolved, it becomes clear to our hero that other adventurers have already crossed this path. Hooray!

She quickly skims a strained narrative introduction, finding it, frankly, quite derivative. Skipping to the operationalizable section of the tome, she finds fast success. This is the tool for them.

With minimal effort, The Test Suite is distributed across 4 machines. Test time is slashed and The Test Suite is saved. Pondering the future, our hero realizes she will never again face uncertainty about scalability. She can always just add more machines!

Test Suite scalability ensured, our heroes go on to face many other adventures and trials. But never again would something come so close to erasing their very core as the oppression of untested code.

## Why distributed-nose?

Scale your tests horizontally across unlimited machines with two test flags.

## Installation

  1. Get the project source and install it

    $ pip install distributed-nose

## Usage

To run half your tests on one machine and the other half on the other:

Machine 1:

$ nosetests –nodes 2 –node-number 1 long_test_suite

Machine 2:

$ nosetests –nodes 2 –node-number 2 long_test_suite

Alternatively, you can use the environment variables: * NOSE_NODES * NOSE_NODE_NUMBER

### Temporarily disabling test distribution

In the case that you’re using environment variables to control test distribution, you sometimes still might want to run a one-off test. Instead of fiddling with environment variables, you can just use the –distributed-disabled flag.

$ export NOSE_NODES=2; $ export NOSE_NODE_NUMBER=1; $ nosetests –distributed-disabled long_test_suite

## Distribution algorithm

To determine which node runs which test, distributed-nose relies on the [hash_ring](https://github.com/Doist/hash_ring) library’s consistent hashing implementation.

By default, tests are individually hashed to the ring. This results in the most even distribution and the best speed if all tests have the same runtime. However, it duplicates class setup/teardown work. If that’s expensive, you may want to use –hash-by-class or set NOSE_HASH_BY_CLASS; this will hash tests in the same class to the same node.

## Running the test suite

The test suite requires nose, and can be run via setup.py:

# python setup.py nosetests

## Is it Awesome?

Yes. Increasingly so.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

distributed-nose-1.2.0.tar.gz (9.1 kB view details)

Uploaded Source

Built Distribution

distributed_nose-1.2.0-py2-none-any.whl (8.4 kB view details)

Uploaded Python 2

File details

Details for the file distributed-nose-1.2.0.tar.gz.

File metadata

  • Download URL: distributed-nose-1.2.0.tar.gz
  • Upload date:
  • Size: 9.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/2.7.15rc1

File hashes

Hashes for distributed-nose-1.2.0.tar.gz
Algorithm Hash digest
SHA256 9e9f201fa9c5ea860ff8adcfbf8f1b3f63fec4597c2c8d8455b79925850a2f3b
MD5 14b072b583b728ca3bf13d160a96bcc3
BLAKE2b-256 8684abfe42ee3001509e230fb4e5e6faf42b7a937e96e32ce76a221d4235bd2b

See more details on using hashes here.

File details

Details for the file distributed_nose-1.2.0-py2-none-any.whl.

File metadata

  • Download URL: distributed_nose-1.2.0-py2-none-any.whl
  • Upload date:
  • Size: 8.4 kB
  • Tags: Python 2
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/2.7.15rc1

File hashes

Hashes for distributed_nose-1.2.0-py2-none-any.whl
Algorithm Hash digest
SHA256 37cbf9b969d3a8e9bef03658f83fc79b748481939311abe8d333b2b5737b4f50
MD5 3054de7e407900e6dd24eca705b71bf7
BLAKE2b-256 4346f91e9f84c0d0013491dfecfa6dd114b751439dbe5c21f5ab3124b75220cc

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page