Testing Exercises Locally
- How do I run the automated tests?
- Where do I write the bonus?
- Why is my import failing?
- How can I manually test my code in the REPL?
- Why is the order of actual and expected flipped in assertEqual?
- Why am I seeing "expected failure"?
- Why am I seeing "unexpected success"?
- What if I'm using Jupyter notebooks?
- Does solving the bonuses require the base problem to still pass?
- What if I can't figure out what's going on?
Please contact me if you have another question.
How do I run the automated tests?
If you'd like, you can download the automated test file and running the tests against your code locally. This way you can store a local copy of your exercises and tests on your computer, write your code in your favorite code editor, and run your tests locally before submitting your solution to the Python Morsels website.
First, download the automated test file (linked on the exercise page). You'll need to make a .py file in your favorite code editor. The filename will need to be the same as the module that's being imported by the test file. If the automated test file is named "test_blorp.py" you'll name your new .py file "blorp.py". These two Python files need to live in the same directory.
For an exercise called add which lives in an add.py module and has a test file called test_add.py, you'll do the following:
- Save the test_add.py tests in the directory you'd like to write your code in
- Using your Python editor of choice, create an add.py file in the same directory
- Write your add function in the add.py module
- Open up your system terminal or command prompt (search Terminal on Linux/Mac or "cmd" on Windows)
- Change directories to the directory your add.py file is in (on Windows you may need to change drives first by typing the drive letter on its own, for example "C:")
- Run "python3 test_add.py" (without the quotes) in that command prompt / terminal Window
- If you're on Windows and Python isn't in your system path, you may need to type "py -3 test_add.py" instead
- If you have another Python path issue (Anaconda sometimes does odd things with Python for example), you'll need to consult Google to figure out how to resolve your Python path issue to run the automated tests against your code.
Where do I write the code for the bonuses?
After solving the base problem, you may want to try one or more of the bonuses (I recommend, and sometimes require, solving them in-order).
You'll work on your bonus problem in the same place you work on the base problem. Make sure your code continues to solve the base problem though, as your bonus tests won't pass if the base problem fails.
Why is my import failing?
After submitting your code in the Python Morsels web app, you might notice that some of your code imports are failing.
If you've imported code from third-party libraries (libraries that aren't included in the standard library which is bundled with Python) you'll see an ImportError.
To get your solution passing, you'll need to figure out how to get your code passing with just the tools that are included in the Python standard library.
How can I manually test my code in the Python REPL?
Getting in the right directory: If you're writing a function or class that you'd like to test from a Python REPL, you'll need to make sure you're in the same directory as your Python module first. If you're using a REPL that you didn't start from your system command prompt, you'll need to change your current working directory. For this reason, I recommend using your system command prompt (CMD on Windows, Terminal on Mac/Linux) and changing directories (cd path/to/my/directory) before starting Python (with "python3" or "py -3" on Windows).
Importing your code: Once you're in the correct directory, you'll need to import the code you're trying to test (e.g. from circle import Circle). Note that if you change your code after you've imported the module, you'll need to exit the REPL, re-enter it, and import your code again. You can't just run your import statement (e.g. "from circle import Circle") again because Python caches modules after they're imported.
Running command-line programs: If you're trying to test a command-line program, you'll need to run your system command prompt (CMD on Windows, Terminal on Mac/Linux). There's no easy way to test a command-line program from a Python REPL.
Why is the order of actual and expected flipped in assertEqual?
It's not, at least not necessarily.
As a community standard the order of actual/expected was discussed in this issue in 2010, but the jUnit world uses the expected/actual order. As BDFL (benevolent dictator for life) Guido van Rossum declared the issue resolved by stating that the order should be shown as "first" and "second" and the meaning should be left up to individual developers to decide on for their own code.
I tend to use the actual/expected order, which is what's most common in Python's official documentation. PyCharm assumes the assertEqual argument order is expected/actual, likely because PyCharm is written in Java and jUnit is very consistent with the expected/actual style (issue here).
TLDR: there isn't an official order for expected/actual or actual/expected in unittest land. If your tooling (*cough* PyCharm) is assuming an order than doesn't match mine, that's an unfortunate side affect of the tool you're using to run the tests.
What is an "expected failure"?
When running the tests against your code, you will likely see "expected failures". These are the tests for the bonuses. To check whether you've passed a bonus you'll want to comment out the "@expectedFailure" line for the appropriate bonus (the first one you find will be the first bonus, second for the second, and so on).
What is this "unexpected success" message about?
If you see an "unexpected success" message, that means you've written code that passes one of the bonuses but you haven't commented out that "@expectedFailure" line.
Normally unittest gives a failure if a test which is expected to fail passes instead. My usage of these @expectedFailure decorators has the unfortunate downside that an accidentally-passing bonus will show a failure message. The unittest library wasn't meant to be used this way and this is one of the biggest limitations to the fairly simple way I wrote the tests.
So if you see "unexpected success", comment out one or more of the "@expectedFailure" lines to see which bonus you accidentally passed.
What if I'm using a Jupyter/IPython Notebook?
If you're not creating Python modules, but writing code in a live coding environment, like a Jupyter Notebook, you'll need to adjust the testing code I've written.
First you'll need to remove the appropriate import lines. For example in the file test_add.py there may be a line "from add import add" which imports the add function you're creating. That line won't work if you haven't made an add module but are instead writing the add function in the same module (notebook) you're running the tests from.
Next you'll need to replace unittest.main() with unittest.main(argv=[""], exit=False)
That line will make IPython not check sys.argv and not raise a SystemExit exception when complete ( more details here).
Does solving the bonuses require the base problem to still pass?
Yes. As you work through the base problem and then the bonuses, you'll need to modify the implementation of your program to keep the initial tests passing while getting the bonuses to pass.
I recommend solving each part separately in-order (base first, then bonus 1, then bonus 2, etc) in order to model how problems often unfold in the real world. First there's a basic requirement, then more is needed, then the requirements change a bit further.
In all the exercises all base tests should allow for subsequent bonuses to pass without causing the base tests to fail.
What if I can't figure out what's going on?
When testing locally, you may want to lookup how to use pdb (the Python debugger) and the breakpoint function (added in Python 3.7). Here are some resources on pdb: