Automating real-world IT tasks with Python, testing, and troubleshooting
Automating real-world IT tasks with Python, testing, and troubleshooting
This domain tests end-to-end automation judgment: turning a manual procedure into a reliable script, making it testable, and diagnosing failures when it runs unattended. Questions often describe a script that works interactively but fails in cron or CI, or ask which test best protects a behavior.
Core rules and concepts
- Design for idempotency: running the script twice should not duplicate users, append duplicate lines, or delete already-moved files. Check state before acting, and use atomic writes or temporary files for critical updates.
- Separate configuration from logic. Read paths, hosts, and credentials from arguments or environment variables, not hard-coded constants.
argparsegives validation and--helpfor free. - Log to stderr or a log file with timestamps and context. Print statements are not a monitoring strategy. Return meaningful exit codes so cron, CI, or an orchestrator can detect failure.
- Handle expected failures specifically: missing files, permission errors, timeouts, and non-zero subprocess exits. Retry only transient errors, with a bounded number of attempts and backoff.
- Test behavior with unit tests.
unittestandpytestboth provide assertions, fixtures, and test discovery. Mock network, clock, and filesystem boundaries, but do not mock the function under test. - Troubleshoot by reproducing the failure, reading the full traceback from the bottom up, isolating inputs, and adding temporary logging. A traceback names the exception type and the call chain.
Worked example
A function renames .txt files to .done in a directory. A useful test creates a temporary directory, writes one file, calls the function, and asserts the new name exists and the old one does not.
def test_rename(tmp_path):
src = tmp_path / 'report.txt'
src.write_text('ok')
rename_txt_to_done(tmp_path)
assert (tmp_path / 'report.done').exists()
assert not src.exists()
Reasoning: tmp_path isolates the test from the real filesystem. The assertions check observable behavior, not private implementation details. Add a second test for a directory with no .txt files and a third for a name collision, such as an existing report.done.
Traps that cost marks
Over-mocking can make a test pass while the real integration fails. Tests that depend on the current time, network, or a fixed absolute path become flaky. Catching every exception and continuing hides failures. A script without a dry-run mode can cause damage on its first production run. Finally, testing only the happy path misses permission, empty-input, and partial-failure cases that dominate real incidents.