Subprocess testing in PyTest - Build an Automation Script
import subprocess import sys import os import pytest def list_directory(): if sys.platform.startswith('win'): cmd = ['cmd', '/c', 'dir'] else: cmd = ['ls'] result = subprocess.run(cmd, capture_output=True, text=True) return result def test_list_directory_contains_this_file(): result = list_directory() assert result.returncode == 0, f"Subprocess failed with code {result.returncode}" # Check that the output contains this test file name this_file = os.path.basename(__file__) assert this_file in result.stdout, f"Output does not contain expected file name '{this_file}'"
The list_directory function runs the system command to list directory contents. It detects the OS to choose the right command (dir for Windows, ls for others).
We use subprocess.run with capture_output=True and text=True to get the output as a string.
The test test_list_directory_contains_this_file calls this function, then asserts the return code is 0, meaning success.
It also asserts that the output contains the current test file name, ensuring the command listed the directory contents correctly.
Assertions include messages to help understand failures.
Now add data-driven testing with 3 different commands: 'ls' or 'dir', 'echo Hello', and an invalid command to test failure.
Practice
Solution
Step 1: Understand subprocess testing
Subprocess testing focuses on running external commands or programs from your code and checking their behavior.Step 2: Identify the correct purpose
Among the options, only checking commands run by your program matches subprocess testing.Final Answer:
To check commands run by your program and verify their output -> Option CQuick Check:
Subprocess testing = check commands run [OK]
- Confusing subprocess testing with UI testing
- Thinking it measures function speed
- Assuming it tests databases
Solution
Step 1: Recall subprocess.run syntax
The correct function is subprocess.run with a list of command arguments and capture_output=True to capture output.Step 2: Check options for correctness
Only subprocess.run(['ls'], capture_output=True, text=True) uses subprocess.run with correct parameters and argument format.Final Answer:
subprocess.run(['ls'], capture_output=True, text=True) -> Option BQuick Check:
Use subprocess.run with capture_output=True [OK]
- Using subprocess.execute which does not exist
- Passing command as string without shell=True
- Using wrong parameter names like capture or output
import subprocess
def test_echo():
result = subprocess.run(['echo', 'hello'], capture_output=True, text=True)
assert result.stdout == 'hello\n'
Solution
Step 1: Understand subprocess.run output for echo
The echo command outputs the string followed by a newline, so stdout will be 'hello\n'.Step 2: Check the assertion
The assertion compares result.stdout to 'hello\n', which matches exactly, so it passes.Final Answer:
The test passes because stdout contains 'hello\n' -> Option DQuick Check:
echo adds newline, assertion matches [OK]
- Forgetting echo adds a newline
- Expecting stdout without newline
- Confusing stdout with stderr
import subprocess
def test_fail():
result = subprocess.run(['false'], capture_output=True, text=True)
assert result.returncode == 0
Solution
Step 1: Understand the 'false' command behavior
The 'false' command always returns exit code 1 (failure), not 0.Step 2: Check the assertion on returncode
The test asserts returncode == 0, which is false, so the assertion fails.Final Answer:
The command 'false' returns a non-zero exit code, so assertion fails -> Option AQuick Check:
'false' returns 1, assertion expects 0 [OK]
- Assuming 'false' returns 0
- Thinking capture_output=True is mandatory for returncode
- Believing shell=True is needed for 'false'
result = subprocess.run(['mycmd'], capture_output=True, text=True)
Solution
Step 1: Understand failure and error output
A failed command has returncode not zero and error messages appear in stderr.Step 2: Match assertion to expected behavior
assert result.returncode != 0 and 'error' in result.stderr asserts returncode != 0 and 'error' in stderr, which correctly tests failure and error output.Final Answer:
assert result.returncode != 0 and 'error' in result.stderr -> Option AQuick Check:
Failure means returncode != 0 and errors in stderr [OK]
- Checking error in stdout instead of stderr
- Expecting returncode == 0 for failure
- Mixing stdout and stderr in assertions
Ready to prove it?
Pass the test and this pattern moves unknown
