@@ -175,6 +175,20 @@ async def close(self) -> None:
175175 """Close the device."""
176176
177177
178+ def set_trait_attributes (
179+ trait : AsyncMock ,
180+ dataclass_template : RoborockBase ,
181+ init_none : bool = False ,
182+ ) -> None :
183+ """Set attributes on a mock roborock trait."""
184+ template_copy = deepcopy (dataclass_template )
185+ for attr_name in dir (template_copy ):
186+ if attr_name .startswith ("_" ):
187+ continue
188+ attr_value = getattr (template_copy , attr_name ) if not init_none else None
189+ setattr (trait , attr_name , attr_value )
190+
191+
178192def make_mock_trait (
179193 trait_spec : type [V1TraitMixin ] | None = None ,
180194 dataclass_template : RoborockBase | None = None ,
@@ -183,12 +197,14 @@ def make_mock_trait(
183197 trait = AsyncMock (spec = trait_spec or V1TraitMixin )
184198 if dataclass_template is not None :
185199 # Copy all attributes and property methods (e.g. computed properties)
186- template_copy = deepcopy (dataclass_template )
187- for attr_name in dir (template_copy ):
188- if attr_name .startswith ("_" ):
189- continue
190- setattr (trait , attr_name , getattr (template_copy , attr_name ))
191- trait .refresh = AsyncMock ()
200+ # on the first call to refresh(). The object starts uninitialized.
201+ set_trait_attributes (trait , dataclass_template , init_none = True )
202+
203+ async def refresh () -> None :
204+ if dataclass_template is not None :
205+ set_trait_attributes (trait , dataclass_template )
206+
207+ trait .refresh = AsyncMock (side_effect = refresh )
192208 return trait
193209
194210
0 commit comments