0

How do I create two different mocked objects with the same spec using the patch decorator?

I have a test that needs two mocked selenium WebElements. I could just use two patch decorators:

@mock.patch("selenium.webdriver.remote.webelement.WebElement")
@mock.patch("selenium.webdriver.remote.webelement.WebElement")
def test_does_not_match_unclickable_element(
    self, invisible_element, inactive_element
):

But in this case, I want to ensure the patched objects are specced out so that any code that might try to call a non-existent method will fail properly.

@mock.patch("selenium.webdriver.remote.webelement.WebElement", autospec=True)
@mock.patch("selenium.webdriver.remote.webelement.WebElement", autospec=True)
def test_does_not_match_unclickable_element(
    self, invisible_element, inactive_element
):
    invisible_element.is_displayed.return_value = False
    invisible_element.is_enabled.return_value = True
    inactive_element.is_displayed.return_value = True
    inactive_element.is_enabled.return_value = False

The following exception raises:

unittest.mock.InvalidSpecError: Cannot autospec attr 'WebElement' from target 'selenium.webdriver.remote.webelement' as it has already been mocked out. [target=<module 'selenium.webdriver.remote.webelement' from '/python3.10/site-packages/selenium/webdriver/remote/webelement.py'>, attr=<MagicMock name='WebElement' spec='WebElement' id='4501588480'>]

How do I pass two copies of the patch mocked object to my test?

Edit: I could use the spec argument directly which has similar results (and would ultimately solve the problem) but....

# This works
@mock.patch("selenium.webdriver.remote.webelement.WebElement", spec=WebElement)
@mock.patch("selenium.webdriver.remote.webelement.WebElement", spec=WebElement)
def test_does_not_match_unclickable_element(
    self, invisible_element, inactive_element
):

I'd like to understand how I can solve the problem utilizing the autospec argument.

Marcel Wilson
  • 3,842
  • 1
  • 26
  • 55

1 Answers1

0

a bit late now perhaps, but for anyone else facing this problem, I used unittest.mock.createautospec with the instance parameter set to True.

This returns an instance of the class being called, and so in the example above, each webelement would be a different object, with all its attributes correctly mocked out.

https://docs.python.org/3/library/unittest.mock.html#create-autospec

miller the gorilla
  • 860
  • 1
  • 7
  • 19
  • It seems that you can add the 'instance=True' to any request for a mock object, and a noncallablemagicmock will be returned. So, in the above question you would have: `@mock.patch("selenium.webdriver.remote.webelement.WebElement", autospec=True, instance=True)` This works for me using pytest, but is not documented in the unittest.mock docs. So, try it and see! – miller the gorilla Mar 16 '23 at 11:21