3

I have a class something like this.

class Upgrade:

    def __init__(self, ssh_client):
        self.ssh_client = ssh_client
        self.channel = self.ssh_client.invoke_shell(width=1000, height=1000)
        self.stdin = self.channel.makefile('wb')
        self.stdout = self.channel.makefile('r')


    def do_upgrade(self):

        # execute some commands on paramiko channel

        for line in self.stdout:
            
            if str(line).startswith("PLAY RECAP"):
                
                # do something

When I trying to mock self attribute called 'stdout' like this(using pytest),

def return_stdout(*args, **kwargs):
    stdout = "\n\rSome return value\n\r"
    return stdout
monkeypatch.setattr(Upgrade, 'stdout', return_stdout)

I'm getting following error.

>     monkeypatch.setattr(Upgrade, 'stdout', return_stdout)
E     AttributeError: <class 'Upgrade'> has no attribute 'stdout'
        

Then, How can I mock 'stdout' using pytest or pytest-mock?

Shirantha Madusanka
  • 1,461
  • 1
  • 11
  • 16
  • the *class* does not have the attribute, you're setting `a` as an *instance* variable. If you want to mock it for the class, declare it there first (`class MyClass: \n a = None \n def __init__(...` , but then the constructor will override the value later on anyway. What are you trying to achieve? – yedpodtrzitko Sep 01 '20 at 02:33
  • Actually I need to test do_upgrade function. class Upgrade: def __init__(self, ssh_client): self.ssh_client = ssh_client self.channel = self.ssh_client.invoke_shell(width=1000, height=1000) self.stdin = self.channel.makefile('wb') self.stdout = self.channel.makefile('r') def do_upgrade(self): for line in self.stdout: logging.info(str(line)) if str(line).startswith("PLAY RECAP"): # do something – Shirantha Madusanka Sep 01 '20 at 02:38
  • please add the code to the original question where it can be formatted/highligted, not like this. – yedpodtrzitko Sep 01 '20 at 02:41
  • Question is updated – Shirantha Madusanka Sep 01 '20 at 02:44

1 Answers1

4

the class does not have the attribute, you're setting stdout as an instance variable. And even if you'd mock it, you'd overwrite it in the constructor (self.stdout = self.channel.makefile('r')). Create a wrapper/property, and monkeypatch that instead:

class Upgrade:
    def __init__(self, ssh_client):
        self._stdout = self.channel.makefile('r')

    def get_stdout(self):
        return self._stdout

then in the test mock the encapsulating method instead:

monkeypatch.setattr(Upgrade, 'get_stdout', return_stdout)
yedpodtrzitko
  • 9,035
  • 2
  • 40
  • 42