Python中如何Mock父类方法测试?现有方案存缺陷求优解
Got it, let's tackle this problem properly. You're right that some of the approaches you tried have drawbacks, but there are two clean, safe ways to achieve exactly what you need without messing with the control flow of Quux.something or risking unintended side effects.
Core Idea
Python uses dynamic attribute lookup, so we can target specific methods on the parent classes (or even individual instances) without modifying inheritance structures or global super behavior.
Approach 1: Patch Parent Class Methods Globally (Per Test)
This works if you don't mind temporarily replacing the parent class methods for the duration of your test. It's clean and doesn't require modifying your class structure.
Here's a complete test case:
import unittest from unittest.mock import patch, MagicMock # Your original class definitions class Foo(object): def something(self, a): return a + 1 class Bar(object): def mixin(self): print("Hello!") class Quux(Foo, Bar): def something(self, a): self.mixin() return super().something(a) + 2 class TestQuux(unittest.TestCase): def test_something_with_class_patches(self): # Mock Bar.mixin to verify it gets called with patch.object(Bar, 'mixin') as mock_mixin: # Mock Foo.something to return a controlled value with patch.object(Foo, 'something') as mock_something: mock_something.return_value = 10 # Set your desired mock return value # Create instance and execute the method under test quux_instance = Quux() result = quux_instance.something(5) # Assertions mock_mixin.assert_called_once() # Verify mixin was invoked mock_something.assert_called_once_with(5) # Verify parent method got correct args self.assertEqual(result, 10 + 2) # Verify final result matches expected logic
Approach 2: Patch Instance-Specific Methods (More Isolated)
If you want to avoid modifying the parent class methods entirely (to prevent side effects on other tests), you can patch the method directly on the Quux instance instead. This is even more isolated:
class TestQuux(unittest.TestCase): def test_something_with_instance_patch(self): quux_instance = Quux() # Mock ONLY this instance's mixin method with patch.object(quux_instance, 'mixin') as mock_mixin: # Still mock Foo.something (since super() resolves to the parent class) with patch.object(Foo, 'something') as mock_something: mock_something.return_value = 10 result = quux_instance.something(5) # Same assertions as before mock_mixin.assert_called_once() mock_something.assert_called_once_with(5) self.assertEqual(result, 12)
Why This Works (And Your Previous Approaches Didn't)
- No inheritance modification: We don't touch
__bases__or redefine class structures, so your original code's logic stays intact. - Targeted patching: We only patch the specific methods we care about (
Bar.mixinandFoo.something), so other uses ofsuper()in your codebase won't be affected (unlike patchingbuiltins.super). - Dynamic lookup: Python resolves
self.mixin()andsuper().something(a)at runtime, so the patched methods are picked up automatically without needing to redefine theQuuxclass.
Key Notes
- For
super().something(a), we need to patchFoo.somethingdirectly becausesuper()inQuux.somethingresolves to theFooclass's method at runtime. - Patching the instance's
mixinmethod (Approach 2) is great for isolation, especially if other tests rely on the originalBar.mixinbehavior.
内容的提问来源于stack exchange,提问作者Xophmeister

