Skip to content
Atlas

Foundations

Check your setup

What the toolchain is, how an exercise is graded, and how to read a unittest failure. Then an easy exercise whose only job is to break loudly now rather than confusingly in module 4.

15 minute read


⚡
For the best experience: Download the native Atlas Desktop app for instant test running, native IDE sync, and 100% offline practice.
Download Desktop App→

Every exercise in this course runs the same way. Taking ten minutes to verify your local environment now prevents tooling issues in later modules. The introductory exercise verifies that Python, the editor, the test runner, and the mock client interact properly on your machine.

The minimal toolchain

The runtime requires three components:

  1. Python 3.10 or newer.
  2. unittest, which ships in the Python standard library.
  3. mockllm, a single-file test mock with zero external dependencies, copied into each exercise workspace.

This design avoids external package managers, virtual environments, and API credentials, keeping tests self-contained and reproducible. The skills required to read unittest outputs translate directly to pytest and other test frameworks.

unittest tests the Python standard library itself. Its output format maps directly to the test reports generated by broader Python testing tools.

How grading works

When an exercise opens, the workspace copies scaffold.py to solution.py. The file solution.py stores your code and persists across editor restarts.

The test file test.py imports directly from solution.py:

from solution import (
    count_words,
    first_reply,
    last_user_message,
    role_counts,
    summarise_messages,
)

The test runner tests both user solutions and reference solutions against the identical test suite. The build pipeline runs reference.py against test.py in continuous integration; if reference code fails any assertion, the lesson cannot build.

Because test.py imports functions by name, preserving function signatures is mandatory. Renaming count_words to word_count triggers an immediate ImportError:

ImportError: cannot import name 'count_words' from 'solution' (/.../solution.py)

Modify function bodies freely, but keep function names and parameter lists intact.

Reading a test failure

Consider a test failure caused by using content.split(" ") instead of content.split():

test_an_empty_string_is_zero (__main__.TestCountingWords.test_an_empty_string_is_zero) ... FAIL
test_collapses_runs_of_whitespace (__main__.TestCountingWords.test_collapses_runs_of_whitespace) ... FAIL
test_counts_words (__main__.TestCountingWords.test_counts_words) ... ok

======================================================================
FAIL: test_an_empty_string_is_zero (__main__.TestCountingWords.test_an_empty_string_is_zero)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/you/exercises/check-your-setup/test.py", line 34, in test_an_empty_string_is_zero
    self.assertEqual(count_words(""), 0)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
AssertionError: 1 != 0

----------------------------------------------------------------------
Ran 16 tests in 0.001s

FAILED (failures=3)

Read the traceback from the bottom:

  1. FAILED (failures=3) indicates the total failure count.
  2. AssertionError: 1 != 0 shows the mismatch. The left value is the output returned by your code; the right value is the expected output.
  3. The lines above the assertion show the test file location and the specific call under test (count_words("")).
  4. The test name test_an_empty_string_is_zero describes the specific behavior being checked.

Custom assertion messages provide additional context:

AssertionError: 1 != 0 : content is None on a turn that only calls a tool, so this has to return a number rather than raise

Text following the colon explains the rationale for the test case.

FAIL indicates an unmet assertion where code executed but returned an unexpected value. ERROR indicates an unhandled exception during test execution, pointing to an exception in solution.py. Resolve ERROR tracebacks first, as fixing exceptions often clears multiple assertion failures.

Running tests from the command line

Run tests from the terminal in the exercise directory:

python3 test.py                                        # run all tests
python3 test.py TestCountingWords                      # run one test class
python3 test.py TestCountingWords.test_none_is_zero    # run one specific test

Isolating a single test case speeds up debugging. When a single logic error fails multiple tests within a class, running the simplest failing test provides the cleanest debugging signal.

unittest executes test methods in alphabetical order. Rather than debugging whichever test appears first in the terminal output, select the test case with the minimal input, such as empty or zero-length arguments.

Troubleshooting Python versions

Verify your installed interpreter version:

python3 --version

If the command prints Python 3.10.x or newer, the setup is complete.

On Windows systems, use the Python launcher py:

py -3 --version
py -3 test.py

If your system has Python 3.9 or older, upgrade to 3.10 or higher. The exercises rely on standard library features introduced in 3.10, including union type annotations (str | None) and sys.stdlib_module_names.

On macOS, install Python via python.org or Homebrew to run alongside the default system interpreter.

Exercise specification

Implement five utility functions for processing chat message sequences:

  1. count_words: count words in a message body.
  2. role_counts: tally message counts per role.
  3. last_user_message: locate the most recent message with role user.
  4. summarise_messages: generate a single-line summary string.
  5. first_reply: execute a single completion call via MockClient.

Pay attention to edge cases:

  • str.split() without arguments collapses consecutive whitespace and returns an empty list for empty strings (""), whereas str.split(" ") returns [""].
  • To find the last user message in multi-turn dialogues, iterate through the message list in reverse order.