Keep Calm and Add Unit Tests with Python

“I just can’t figure out why my program doesn’t work. Can someone help me?”

“I’ve been working on this for hours and I’m still stuck.”

“I had it working a few minutes ago, but I did something and now nothing works!”

Making Your Own Private Hell

You probably mastered a few basics, like writing a simple Python program. You’ve gained confidence that you can write a loop or a function. You can see how a basic Python program works. You are hungry for more.

Maybe you are plowing through a book or a course on Python. Maybe it’s a class you’re taking. Or maybe you are getting ambitious and you decide to create something new, like a Reddit bot.

You are busy adding working code. You test every now and then by running the program. If there’s a problem, you edit the file and run the program again. Everything seems so easy.

Then you realize something is wrong. That part you tested 10 minutes ago, by hand, but didn’t test until just now… it’s not working. Are you just imagining this? No, you try it again. It’s still wrong. It’s still broken. If you run the program 10 more times, magic will happen and it will work. Right?

Again and again you try to fix the problem, but it only ends up somehow worse than before. Minutes turn into hours. Time is slipping by. You are so frustrated you are ready to hurl your computer out the nearest window. Why did you think you could do this programming thing anyway? It’s impossible! Only computer geniuses could keep all this straight. And this is just a basic program!

“Is it too late to get my money back?”, you think.

The Way Out

What if you could avoid all of this misery? Imagine if you could use Python itself to check if your new program was correct every time you made a change? As soon as you made any change, you could check to see if you made a mistake. You could find out right away if the change you just made made things better or worse?

Think about this: if you can check your program after every change, and you keep the changes small, then you only have to undo a small change to get back on track. This would keep you from wasting your time with big changes that will derail you for hours or days.

You should write automated software tests. When I say “automated” I mean tests you don’t have to keep in your meat brain. You just have to remember to run a single command to run every test instead of remembering to run a program multiple times to check every situation.

There’s another important benefit to these tests – it’s a professional habit. Writing tests differentiates the pros from the slobs. Writing tests for your program is like basic sanitation for doctors. Would you trust a doctor to perform surgery without washing their hands first?

Writing Tests – A Detailed Example

In this example, we already have a Python source file called rational.py.

For now, I just want you to get comfortable with the tools of creating and then running your first real tests. That’s what you’ll learn today.

You’ll get the most out of this example by following along and running the examples. The original program is located here. If you are familiar with git, you can clone the repository. Otherwise you can just copy the source code locally.

First let’s look at files in the original project.

The rational.py is the implementation and rationaltest.py is a test driver.

The test driver in this project is a “poor man’s” test framework. It’s functional, sure. If you run the file you’ll get a result like this:

We’re going to use the rationaltest.py to make our first tests easy to write. You only have to concern yourself with how to construct the tests using a testing framework. We’ll use pytest for this example.

Let’s run pytest and see what happens:

Just as we expected, no test were run. Let’s add the file that will hold the tests. Let’s call the test file test_rational.py. The name is important.

As mentioned in the pytest documentation, the framework uses standard test discovery to find and run tests.

We’re concerned with 2 basic rules in this example:

  1. files named test_*.py in the directory
  2. files named *_test.py in the directory

Let’s run the pytest command again with our new (empty) test file, test_rational.py.

Again, no tests were detected, so no tests ran. Let’s add a test.

If we use the existing smoke test file as a guide, you’ll see a series of tests lists. As a general rule, it’s easier to test the behavior of code that has the fewest dependencies. What do I mean by this?

Looking at the Rational class, you might be tempted to start testing there. However, you’ll see that the implementation of that class depends on another function, gcd.

If a test of the Rational class fails we can’t be sure that the class is responsible for the error. At least not yet. We should drill further into the dependency of the class and start with the “bricks” the foundation of the Rational class is built upon.

This is why gcd is a good candidate. The comments in the function tell us that it’s based on Euclid’s algorithm for finding the greatest common denominator (gcd) of 2 integers.

Since we know the general expectations of how gcd works, we can start with a simple test case. On paper, Euclid’s algorithm predicts the gcd(3,0) will be 3.

Our test file now looks like this:

import rational

def test_gcd_a_0_is_0():
assert True

If we run pytest again:

So far, so good. Let’s replace the assertion with a real test, but with a clearly wrong answer.

import rational

def test_gcd_a_0_is_0():
assert rational.gcd(3,0) == 100000

You might wonder why would add a test that is going to fail. We want to verify that the test we are creating is going to run and that if there is an error in the test we’ll catch it.

Consider how blind we would be if we created a faulty test and the test framework ignored the failing test. How much time would we waste building on this false assumption? It’s better to check now, at the start, and catch stupid mistakes before they cost us an arm and a leg.

Isn’t that great? pytest tells you exactly where it fails and why. Let’s repair the test with the correct expected value.

import rational

def test_gcd_a_0_is_0():
assert rational.gcd(3,0) == 3

Then verify that with pytest:

That’s it, you did it! You created a test and know you know how to build more. Instead of trying to remember each test case and run them manually, you only have to run one command to run all the tests.

That feeling you have right now? That’s the feeling of victory over chaos.

How to Level Up in Python

How do I, someone who just dabbles in Python and uses it for daily data handling tasks, become an expert in Python?

Anytime you learn a new skill, such as learning Python, you might feel anxiety about wether or not you are “ready”. You’re always looking around at this course or that, wondering “Is this any good?” or “Will I learn the right things?”

You want some way to hone your skills and “level up”. In school you might have a math class where your teacher would throw problem sets at you. You had lots of opportunities to work problem after problem, making corrections and getting (hopefully!) better. Now you know the basics of Python. You can even write a few Python programs by yourself. But, where to go from here? How can you take things to the next level?

An Excellent Way to Improve Your Python Skills

If you want to develop real skill in Python, you need to write as many Python programs as you can. You also need to get feedback about how effective your programs are. There’s no sense in writing defective garbage if you want to get better. You need some way to write lots of programs, get feedback, and be pushed to improve.

Luckily, there’s a superb place for this call Exercism.io.

Exercism.io is a web site that “provides countless small wins”. This is brilliant!

When you are working hard to improve, each win is a bit more momentum to push forward. Believe me, as you progress, you will need to be pushed a little harder to get better each time.

Exercism.io gives you a a lot of Python challenges – over 100 – for you to conquer. Each challenge comes with the tests already written. So in addition to learning how to solve problems in Python, you get to see how unit tests are built along the way.  If you want to become an expert Python programmer then you must learn to write tests.

A Walkthrough of the First Challenge

Go ahead and sign up using your Github account. You’ll be asked to accept terms and conditions. Once you’ve done that, you should see a list of tracks. Find the Python track and click it.

Click the “Join the Python track” on the next screen.

You’ll see a dialog that asks if you want to use Mentored Mode or Independent Mode. Select Mentored Mode if you would like to a more structured experience where others provide feedback. Independent Mode is more of a work-at-your-own-pace model.

You will then see the page for your Python track.

Click on the “Hello World” section. You’re almost ready to install the command line interface (cli) tool. On the next page, look for and click the “Begin walk-through” option.

This will start a short series of steps that will guide you to installing the command line tool, which you will need to submit your answers and demonstrate progress.

To complete the installation, you will need your Exercism.io API key.

I created a directory called

exercism

and then another sub-directory under that called

python

.
You’ll next fetch the first challenge (in the

exercism\python

directory:

If you list the contents of the hello-world directory you’ll see:

hello_world.py  hello_world_test.py  README.md

It’s tempting to jump right in and start writing the implementation in

hello_world.py

. Let’s set a good habit right now: run the tests first!

The README.md contains interesting information about how to run the tests. Assuming I’m using Python 2.7, then I would run:

You just ran the tests (pre-written by the challenge author) but since there is no implementation the tests failed. Our next step is to provide that implementation.

Every time we make an update we should run the tests, as above. We’ll know very quickly if we made progress or introduced a bug (a regression).

Here’s an example implementation you can use in

hello_world.py

:

def hello(name=''):
    return 'Hello, World!'

Let’s rerun the tests and see how we did:

And that’s that. Let’s push our solution back to Exercism.io:

Your python solution for hello-world has been submitted.

Programmers generally spend far more time reading code than writing it.
To benefit the most from this exercise, find 3 or more submissions that you can
learn something from, have questions about, or have suggestions for.
Post your thoughts and questions in the comments, and start a discussion.
Consider revising your solution to incorporate what you learn.

Yours and others’ solutions to this problem:
http://exercism.io/tracks/python/exercises/hello-world
I bet you’ll be ready to devour the next one. Good luck!

If you want a more detailed example of writing tests, check out Keep Calm and Add Unit Tests with Python.

Arrrgh! Why Doesn’t My Python Program Work?

I’m having trouble with a tutorial… it doesn’t work!

I can’t figure out why my code isn’t working!

You’ve been banding your head against your desk for hours. The examples seemed so clear, but when you wrote your program it all fell apart. You’ve looked over the code, again and again. Everything looksfine. IT. JUST. DOESN’T. WORK.

You want your program to work correctly. You want to move past this annoying bug. You want to move on and keep learning. What can you do?

You can do a few things when you are stuck. You can write tests while while you write the program. You can talk to someone (or yourself!) else and get their help.

The Solution!

Use a debugger. A debugger allows you to interactively run a program and check the process state. Use a debugger to quickly explore how a program actually works. A debugger helps you understand Python better than passively reading example programs.

A Detailed Walkthrough

In this case, we will use the built-in Python debugger. The Python program we’re running is from a Reddit post, and the code is here.

First, in your console, start your program (we’re using Python 3.6 at the moment):

This starts the Python interpreter and loads the pdb (the Python debugger) module.

You’ll see the pdb prompt:

This shows you that the program execution starts at the main function definition.

The debugger accepts a number of different commands. The first one we’ll use is the w command – which stands for where, and it will show you the current “line” in the program the debugger is about to execute. It also shows you the current “stack”, or the history of functions the interpreter executed in the program.

(Pdb) w c:\program files\python36\lib\bdb.py(431)run() -> exec(cmd, globals, locals) (1)() > c:\users\jjeffers\pydebug\guesser.py(1)() -> def main():

(Pdb)

The bottom most function is the current executing “frame” of the program.

To get to the next line of the program (and have Python execute the current line), press n (or next).

> if __name__ == “__main__”:
(Pdb)

Notice how the function definition was “skipped”, and the debugger jumped to the next executable line.

Enter n again.

Now we’re entered the if statement, because the test for

was true. The debugger is showing you that the next line to execute is the call to the “main” function.

Enter s (or step) here. The step command works like next, but instructs the debugger to “step into” any function calls.

Don’t worry, we’re not caught in a loop here. Enter n to move to the next line inside the main function.

-> print(“Guess a number between 1 and 100.”)
(Pdb)

Keep using n until you reach the line:

-< while not found:
(Pdb)

You will see the lines for the variable definitions for found and randomNumber. You can use the debugger to query the interpreter for the current value of a variable.

Let’s move on until you get to the prompt:

-> userGuess = input(“Your Guess: “)
(Pdb)

Enter n again, then and enter 3 when prompted.

The very next line is

-> if userGuess == randomNumber:
(Pdb)

Here’s where the debugger really gets useful. Go ahead and query the test expression:

(Pdb) userGuess == randomNumber
False

This shows you where the issue is, but it might still not be clear why that test fails until we look at the operands userGuess and randomNumber.

(Pdb) userGuess ‘3’ (Pdb) randomNumber

3

And that’s it – the 2 variables hold different values – one is a string and the other is an integer.

Using the debugger is a good skill to learn. It helps you track down issues in a running program. If you want to add more safety, you need to write tests, too.

If you want a detailed example of writing a test, check out Keep Calm and Add Unit Tests with Python.

View all posts by James Jeffers

Published March 5, 2018February 1, 2019

First Steps: Write Your First Python Program in 30 Seconds

If you want to become a test automation engineers but are still thinking about learning how to program you need to get moving.

You need to take the first step. You need to push a little rock that will start an avalanche of momentum.

You are going to write your first program right now.

Go to Python.org and follow along:

You do not have sufficient freedom levels to view this video. Support free software and upgrade.

This is an embedded Python interpreter. It’s probably not a tool you would use for anything beyond simple experimentation. You have to admit, it’s a very quick way to try your hand at some simple Python programs.

Introduction to Programming Course Comparison

There are many courses available to help you learn how to program.

Here is a list of several available online, in no particular order. None of these courses assume you have previous programming experience.

DISCLAIMER: I receive no compensation for these summaries.

Automate the Boring Stuff (Udemy)

Cost: $10 for the Udemy video course if you go through the link at https://automatetheboringstuff.com/, otherwise $50.

Time to Complete: 9.5 hours

Summary: Comprehensive review of Python through video lectures. The author says the video course covers most of the same ground as the book, but the book’s probably a great alternative if you prefer that medium.

Free Programming Basics Course (Ministry of Test)

Cost: Free

Time to complete: a few hours.

Summary: No frills survey of programming concepts and tools. Course material is delivered by web content. There is no feedback or interaction with an instructor.

Programming for Everybody (Getting Started with Python) (Coursera)

Cost: Free 7-day trial, $49/mo after trial ends.

Time to complete: 6 weeks, 2-4 hours/week.

Summary: Long distance entry level college course. Python is the language used to illustrate concepts with videos, web content, and proprietary courseware. Assignments are graded as pass/fail by a auto-grader process.

Programming Foundations with Python (Udacity)

Cost: Free

Time to complete: 6 weeks.

Summary: Self-paced low-level programming course with video instruction, proprietary courseware, discussion forums. Also features quizes and forum interaction for feedback. “Nanodegree” offered for completion of a curriculum group. Favors lecture format with worked examples by the instructor over interactive application of Python by the student.

Master Fundamentals of Programming for Beginners (Udemy)

Cost: $194.00

Time to complete: 13 hours.

Summary: Comprehensive programming course that introduces C and Python. Relies on video lectures and a “Q&A” feature to review and search questions submitted by other students. Little opportunity to write programs and get feedback.

Try Python (Code School)

Cost: $29/mo, but some courses free

Time to complete: 2-3 hours

Summary: Self-paced entry level programming course focused on Python basics. Features videos, slide downloads, proprietary courseware, and an interactive Python emulator.

Learn Python (Code Academy)

Cost: Free, optional upgrades ($19/mo and $199) for access to technical support, more lessons, and additional material.

Time to complete: 10 hours

Summary: Self-paced entry level programming course focused on Python basics. Features web content, proprietary courseware, and an interactive Python emulator.

Ruby in Twenty Minutes (ruby-lang.org)

Cost: Free

Time to complete: 20 minutes

Summary: Very quick “up and running” tutorial. Assumes you have already installed Ruby and are comfortable with the command line. Nothing fancy here – just enough to wet the appetite for the language.

What is Programming? (Khan Academy)

Cost: Free

Time to complete: less than an hour

Summary: Similar to other Kahn Academy lessons, shows you the theory behind programs, and then begins to dig into some Javascript to manipulate images in an interactive emulator. A non-threatening introduction before getting into the deep end of the pool.

Introduction to Computer Science and Programming using Python (Edx/MIT)

Cost: Free. Optional certificate for $49.00, accredited tuition rate of $300. Textbook (available from amazon.com) is not included in the cost.

Time to complete: 9 weeks, 15 hours/week.

Summary: Self-paced college level course featuring introductory computer science concepts. Designed for students not majoring in CS or EE degree programs. Features lectures, interactive assignments, problem sets, and quizes. A certificate of completion is available (see Cost section). Credit hours available for qualified students.

Reactions to “What’s the best programming language to learn for test automation?”

Previously I wrote what I thought about the best programming language to learn for test automation.

Spoilers: I picked Python.

I posted links to that article on a test automation discussion site. People also posted links to the article on social media.

A couple of reactions on Twitter:

Mark and Brian raise valid counterpoints. Of course, the real answer is always “It depends!”

An expert knows exactly the right tool to select because they have experience to inform that choice. A beginner has almost no experience and their choices are arbitrary.

Sometimes a beginner becomes so overwhelmed by the number of choices, they become paralyzed with indecision. For a beginner, it’s often far more important to get clear direction and get some small victories under their belt. It’s about gaining momentum.

For a beginner, any choice is perfectly fine as long it gets them started. Once a beginner learns one programming language, picking up the next one will be easier.

It’s kind of like the old saying about exercise: “Which exercise is the best? The one you will do.”

What’s the best programming language to learn for test automation?

People ask this question in other ways, too.

How do you choose a programming language for software testing automation?

Does it matter that I am writing automation code in a different language to that used in development?

How would you rank [insert language here] and what is the most popular language used in building automated frameworks?

In the “real world”, the tool you use to build a test automation tool depends on a lot of things – who are you working with, how much experience do you have with the system under test, is the system a web application or an API, etc.

If you are just starting out then the first thing you must do is to learn how to program.

When you are just starting to learn about programming there are many choices. You are not sure which path is the right path. Everything looks like it is important. You are not sure which way to go or what to do.

You need a clear answer. You need an answer that pushes aside all of the doubt and stress about picking the right thing.

The short answer is: Python.

Python has a lot of advantages for someone learning how to program.

You can write short programs and run them to get quick feedback with Python. Fast feedback means you don’t spend time waiting to see if what you did was right or wrong. You learn and correct mistakes quickly.

Python’s syntax (the rules of how a program is structured) is similar to other programming languages such as Java, and C#. Once you learn the patterns of how a Python program is built, you’ll learn your second language much faster.

Python also enjoys a lot of help for the beginning programmer.

Here are some of the books I recommend:

There are many more out there, but this list should be a good starting point.

Let me know if this was helpful.