Python单元测试:如何Mock传入函数的OpenZWave Network类对象?
Let's walk through exactly how to isolate and test your get_temperature function without needing a real Z-Wave network or sensors. The key is correctly mocking the nested OpenZWave objects—your earlier attempts with Mock/MagicMock likely failed because you didn't fully replicate the nested property chain (network → nodes → multisensor → values). Here's a step-by-step solution using Python's standard libraries, plus optional tools to simplify things.
Step 1: Break Down the Dependency Chain
First, let's map out what your function actually needs from the network_obj:
network_obj.nodes[sensor_id]: A multisensor objectmultisensor.get_values(): A method call (no return value needed, just needs to be callable)multisensor.values: A dictionary of value objects, each with alabelanddataattribute
We can mock each of these layers explicitly to control exactly what data is returned.
Step 2: Explicit Mocking (Simplest for Your Case)
Instead of using patch right away, let's build mock objects from the bottom up. This makes it easy to see exactly what's being simulated.
Here's a complete test case using unittest and unittest.mock:
import unittest from unittest.mock import Mock, MagicMock from your_module import get_temperature # Replace with your actual module name class TestGetTemperature(unittest.TestCase): def test_extracts_correct_temperature(self): # 1. Mock the individual temperature value object temp_value = Mock() temp_value.label = "Temperature" temp_value.data = 25.5 # Sample temperature reading # 2. Mock other sensor values (to simulate a real multisensor) humidity_value = Mock() humidity_value.label = "Humidity" humidity_value.data = 62 # 3. Mock the multisensor itself multisensor = Mock() multisensor.values = { "temp_key": temp_value, "humidity_key": humidity_value } multisensor.get_values = MagicMock() # Mock the method call # 4. Mock the network object network_obj = Mock() network_obj.nodes = {"sensor_123": multisensor} # Match your test sensor ID # 5. Run the function under test result = get_temperature("sensor_123", network_obj) # 6. Verify results and method calls self.assertEqual(result, {"Temperature": 25.5}) multisensor.get_values.assert_called_once() # Ensure get_values() was triggered def test_handles_missing_sensor_id(self): # Test what happens when the sensor ID doesn't exist network_obj = Mock() network_obj.nodes = {} with self.assertRaises(KeyError): get_temperature("invalid_sensor", network_obj) def test_handles_no_temperature_value(self): # Test when the sensor has no temperature reading multisensor = Mock() multisensor.values = {} multisensor.get_values = MagicMock() network_obj = Mock() network_obj.nodes = {"sensor_123": multisensor} result = get_temperature("sensor_123", network_obj) self.assertEqual(result, {})
Step 3: Using patch for Class-Level Mocking
If your function is creating or importing an OpenZWave Network class directly, you can use @patch to replace the entire class with a mock. Here's how that would look:
from unittest.mock import patch class TestGetTemperatureWithPatch(unittest.TestCase): @patch("your_module.Network") # Replace with where Network is imported from def test_with_patch(self, mock_network_class): # Get the mock network instance mock_network = mock_network_class.return_value # Build the same mock multisensor as before temp_value = Mock() temp_value.label = "Temperature" temp_value.data = 22.0 multisensor = Mock() multisensor.values = {"temp": temp_value} multisensor.get_values = MagicMock() # Attach the multisensor to the mock network's nodes mock_network.nodes = {"sensor_456": multisensor} # Run the test result = get_temperature("sensor_456", mock_network) self.assertEqual(result, {"Temperature": 22.0})
Recommended Tools
- Python's built-in
unittest.mock: This is all you need for basic mocking—it's part of the standard library, so no extra installs required. - pytest + pytest-mock: If you prefer pytest over unittest, the
pytest-mockplugin provides amockerfixture that simplifies mock setup (no need to import Mock/MagicMock directly). Example:def test_with_pytest_mock(mocker): temp_value = mocker.Mock(label="Temperature", data=24.0) multisensor = mocker.Mock( values={"temp_key": temp_value}, get_values=mocker.MagicMock() ) network_obj = mocker.Mock(nodes={"sensor_789": multisensor}) result = get_temperature("sensor_789", network_obj) assert result == {"Temperature": 24.0}
Key Takeaways
- Always mock the entire dependency chain: Don't skip layers (e.g., don't mock just
network_objwithout setting up itsnodesattribute). - Use
MagicMockfor methods you need to verify were called (likeget_values()). - Test edge cases: Missing sensors, missing temperature values, etc.—this makes your tests more robust.
内容的提问来源于stack exchange,提问作者Gubimand

