Skip to content
← DevOps foundations

Practical lab guide

Lab: build and test a configuration-reporting CLI

Combine validation, file handling, command output, and focused tests.

Documentation reviewed2026-10-01 · 3 min read · lab time varies
On this page

Assemble the fixture

Use Python 3.11 or newer. Place capacity.py, read_capacity.py, and capacity_cli.py from the preceding bites in one new directory. Add a tests directory. The tool reports a supplied capacity fixture; it does not inspect or modify your actual disk.

Save tests/test_capacity.py:

python
import json
import tempfile
import unittest
from pathlib import Path
from capacity import remaining_gib
from read_capacity import read_capacity

class CapacityTests(unittest.TestCase):
    def test_remaining(self):
        self.assertEqual(remaining_gib(20, 8), 12)

    def test_rejects_invalid_range(self):
        with self.assertRaises(ValueError):
            remaining_gib(20, 21)

    def test_rejects_boolean(self):
        with self.assertRaises(TypeError):
            remaining_gib(20, True)

    def test_reads_file(self):
        with tempfile.TemporaryDirectory() as directory:
            path = Path(directory) / 'capacity.json'
            path.write_text(json.dumps({'total': 20, 'used': 8}), encoding='utf-8')
            self.assertEqual(read_capacity(path), 12)

    def test_rejects_unknown_field(self):
        with tempfile.TemporaryDirectory() as directory:
            path = Path(directory) / 'capacity.json'
            path.write_text('{"total":20,"used":8,"extra":0}', encoding='utf-8')
            with self.assertRaises(ValueError):
                read_capacity(path)

From the project root, run python3 -m unittest discover -s tests -p 'test_capacity.py' -v. Confirm that five capacity tests were discovered. Change one expected value temporarily and verify the suite fails, then restore it. This is a useful check that the test invocation actually executes assertions.

Read what each test establishes

The first test checks the known calculation. The next two make invalid input observable, including Python's otherwise surprising treatment of booleans as integers. The file tests create isolated temporary directories, write only invented data, and remove those directories automatically when the context exits. They do not depend on your real disk capacity or a particular working directory's fixture file.

Use python3 -m unittest discover -s tests -p 'test_capacity.py' -v when you want exactly these five tests. If you retained the preceding HTTP lesson's test_status.py, unfiltered discovery also runs its two tests; that larger count is expected. A count of zero means discovery missed the files and is not a successful check of the tool.

Keep failure scope clear: rejecting a missing field is a file/schema check; accepting a valid calculation is a function check; returning the right exit code requires a CLI check. None of the five tests above launches the CLI process.

Exercise the CLI boundary

Run the CLI with a valid JSON fixture, a malformed JSON file, a missing file, and no argument. Capture stdout, stderr, and exit status separately. Add tests for these boundary behaviors as an extension; the five tests above do not yet test the subprocess interface.

Complete the loopback HTTP exercise independently. Record which failures were parser errors and which required a running service. Do not add cloud credentials to this tool.

Checkpoint and cleanup

Explain why importing the calculation module does not run its example loop, why a parsed JSON object still needs validation, and why failure must propagate to the caller. Keep a README with the exact invocation, output schema, and limitations.

Temporary directories inside the tests clean themselves up. Stop any HTTP fixture and remove its named files. Retain this small project for the CI lab. Revision: pure logic, validated boundaries, useful errors, real assertions, no external side effects in unit tests.

Answer the checkpoint

The main guard keeps the example loop out of imports. Parsing establishes JSON syntax, while our key/type/range checks establish this tool's expected data. A nonzero exit lets the shell or CI stop dependent work. These are three separate behaviors; point to the line implementing each rather than answering only with a definition.

For the CI lab, carry capacity.py, read_capacity.py, capacity_cli.py, and tests/test_capacity.py into the practice repository. The HTTP fixture and its tests can remain a separate extension. Do not copy .venv, temporary output files, or credentials.

Apply this to MicroBank

After the six foundations modules, apply Python to the real local transaction flow. Start with the project setup and follow its ordered steps; later implementation stages depend on the earlier files.

Sources and practice status

Primary references: unittest↗; TemporaryDirectory↗.

Reviewed against documentation on 1 October 2026. The exercises still need to be run in your environment. Record your results and tool versions in your notes.

Your notes and evidence

Record observations, questions, or links to your work. Keep credentials out of your notes.

Loading saved progress…

Back up or restore this path

Progress and notes stay in this browser. A backup contains only this learning path.