arabelatso/python-test-updater
Updates Python test code to work with new versions of the code being tested. Use when Claude needs to: (1) Update tests after code changes, (2) Fix broken tests due to signature changes, (3) Update assertions to match new behavior, (4) Add test cases for new functionality, (5) Analyze code differences and their test impact, (6) Run tests and fix failures based on error messages. Takes old code, new code, and old tests as input, outputs updated tests that pass.
npx skills add https://github.com/ArabelaTso/Skills-4-SE --skill python-test-updater
Update Python tests to work correctly with new code versions.
Gather required inputs:
Verify inputs:
Automated analysis:
python scripts/analyze_code_diff.py <old_file> <new_file>
Manual analysis:
Identify change types:
Function signature changes:
Return value changes:
Behavior changes:
Class changes:
Async changes:
See test-update-patterns.md for detailed patterns.
Read the old tests:
Identify test components:
Map tests to code:
For each code change, identify test impact:
Signature changes → Update function calls
# Old code: function(arg1, arg2)
# New code: function(arg1, arg2, arg3=default)
# Old test
result = function(value1, value2)
# Updated test
result = function(value1, value2) # Works with default
# OR
result = function(value1, value2, value3) # Explicit value
Return value changes → Update assertions
# Old code: return value
# New code: return {"result": value, "status": "ok"}
# Old test
assert result == expected_value
# Updated test
assert result["result"] == expected_value
assert result["status"] == "ok"
Behavior changes → Update expected values
# Old code: validates length >= 6
# New code: validates length >= 8 and has digit
# Old test
assert validate("abc123") == True
# Updated test
assert validate("abc12345") == True # Updated
assert validate("abc123") == False # Now fails
New functionality → Add new tests
# New code: added get_display_name() method
# Add new test
def test_get_display_name():
obj = MyClass("value")
assert obj.get_display_name() == "Value: value"
Apply updates systematically:
Step 1: Update imports if needed
# If new exceptions or classes added
from module import NewException, NewClass
Step 2: Update function/method calls
Step 3: Update assertions
Step 4: Update exception handling
# Old
with pytest.raises(OldException):
function()
# New
with pytest.raises(NewException):
function()
Step 5: Update async/await if needed
# Old
def test_function():
result = function()
# New (if function became async)
@pytest.mark.asyncio
async def test_function():
result = await function()
Step 6: Add new test cases
Execute the updated tests:
# Run all tests
pytest test_file.py
# Run specific test
pytest test_file.py::test_function
# Run with verbose output
pytest -v test_file.py
Check results:
If tests still fail:
Analyze error messages:
Common failure types:
1. AssertionError
AssertionError: assert 10 == 15
→ Expected value changed, update assertion
2. TypeError
TypeError: function() missing 1 required positional argument: 'new_param'
→ Add missing parameter to function call
3. AttributeError
AttributeError: 'dict' object has no attribute 'field'
→ Return type changed, update how result is accessed
4. ImportError
ImportError: cannot import name 'OldClass'
→ Class renamed or removed, update import
Fix each failure:
Final verification:
Refine if needed:
Example refinement:
# Before
def test_function_case1():
assert function(5) == 10
def test_function_case2():
assert function(10) == 20
# After (parametrized)
@pytest.mark.parametrize("input,expected", [
(5, 10),
(10, 20),
])
def test_function(input, expected):
assert function(input) == expected
Code change:
# Old
def function(a, b):
return a + b
# New
def function(a, b, c=0):
return a + b + c
Test update:
# Old test (still works)
def test_function():
assert function(1, 2) == 3
# Add new test for new parameter
def test_function_with_c():
assert function(1, 2, 3) == 6
Code change:
# Old
def get_data():
return [1, 2, 3]
# New
def get_data():
return {"data": [1, 2, 3], "count": 3}
Test update:
# Old
def test_get_data():
data = get_data()
assert len(data) == 3
# New
def test_get_data():
result = get_data()
assert len(result["data"]) == 3
assert result["count"] == 3
Code change:
# Old
def validate(value):
return len(value) >= 6
# New
def validate(value):
return len(value) >= 8 and any(c.isdigit() for c in value)
Test update:
# Old
def test_validate():
assert validate("abc123") == True
assert validate("abc") == False
# New
def test_validate():
assert validate("abc12345") == True # Updated
assert validate("abc123") == False # Now fails validation
assert validate("abcdefgh") == False # No digit
assert validate("abc") == False
Code change:
# Old
def fetch():
return data
# New
async def fetch():
return await async_data()
Test update:
# Old
def test_fetch():
result = fetch()
assert result is not None
# New
@pytest.mark.asyncio
async def test_fetch():
result = await fetch()
assert result is not None
Solution:
Solution:
Solution:
Solution:
Old code:
def calculate_price(quantity, unit_price):
return quantity * unit_price
New code:
def calculate_price(quantity, unit_price, discount=0.0):
subtotal = quantity * unit_price
return subtotal * (1 - discount)
Old test:
def test_calculate_price():
price = calculate_price(5, 10.0)
assert price == 50.0
Analysis:
discount with default value 0.0Updated test:
def test_calculate_price():
# Test without discount (original behavior)
price = calculate_price(5, 10.0)
assert price == 50.0
def test_calculate_price_with_discount():
# Test with discount (new behavior)
price = calculate_price(5, 10.0, 0.1)
assert price == 45.0 # 50 * 0.9
Verification:
pytest test_file.py -v
# Both tests should pass
Provide updated test code with:
Take arabelatso/python-test-updater from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.