Search code examples
pythonunit-testingclasstestingpytest

Testing static methods with pytest


I am trying to test the write_nodes static method from PrunedGraphWriter (please see below). The original function copies files (nodes) from one dir to another, whereas the mocked function just checks that the nodes to be written are what I expect in the test case.

I am getting:

FAILED .spyder-py3\app\tests\test_pipelines.py::TestSieve::test_sieve - TypeError: 
TestSieve.test_sieve.<locals>.mock_pruned_graph_write_nodes() takes 3 positional arguments but 4 were given

This error does not occur and all tests pass if i pass self to mock_pruned_graph_write_nodes(). However, I don't see why this is - as I understand, there is no explicit expectation of a self arg when using static methods.

Can someone explain what is going on? Thank you!

networks.py

class PrunedGraphWriter:
    ...
    def __init__(self, args) -> None:
        ...
        self.written_nodes = self.write_nodes(self.nodes, 
                                              input_genbank_dir, 
                                              output_genbank_dir
                                              )
    ...
    @staticmethod 
    def write_nodes(nodes : list, input_genbank_dir : str, 
                    output_genbank_dir : str) -> list:
        ...

pipelines.py

from app.networks import PrunedGraphWriter 
def sieve(args):
    ...
    pruned_graph = PrunedGraphWriter(args)
    ...

test_pipelines.py

class TestSieve:
    ...
    def test_sieve(self,monkeypatch, caplog...):
        def mock_pruned_graph_write_nodes(nodes, 
                                          input_genbank_dir, 
                                          output_genbank_dir) -> list:
            assert nodes == ['file3', 'file4']
            return nodes
        ...
        monkeypatch.setattr('app.pipelines.PrunedGraphWriter.write_nodes', 
                             mock_pruned_graph_write_nodes)
        ...
        with caplog.at_level(logging.INFO):
            sieve(args)
        ...

Solution

  • I believe that the issue is I am mocking a static method with a normal method. As the normal method is called within the PrunedGraphWriter class, it now expects a self parameter (as there is no @staticmethod decorator for the mock method).

    The issue also goes away when I put a @staticmethod decorator on the mock method, which supports this:

    def test_sieve(self,monkeypatch, caplog, ...): 
        @staticmethod
        def mock_pruned_graph_write_nodes(nodes, input_genbank_dir, 
                                          output_genbank_dir) -> list:
            ...