'assert_called_with always picking up the arguments from the last call

I am very new to python and this is probably something trivial.

I have the following test:

import pytest
from pytest_mock import MockerFixture, call

# Create environment before importing anything from app/.
import makeenv

from data_f import balance_ledger_functions
import orm

from mock_orm import mock_nodes_db

def test_balance_ledger_process_settled(mock_nodes_db: None, mocker: MockerFixture) -> None:
   settled_tranaction = created_transaction
   settled_tranaction["recent_status"]["status_id"] = "4"

   spy = mocker.spy(orm.Nodes, "balance_update")

   assert balance_ledger_functions.balance_ledger(created_transaction) == settled_tranaction

   to_node_id = settled_tranaction["to"]["id"]
   amount = settled_tranaction["amount"]["amount"]
   update_transaction_payload = {"balance":"{0}".format(-int(float(amount))), "is_cma" : False, "currency" : "cUSD"}
   spy.assert_called_with(to_node_id, update_transaction_payload)
   
   # fees 
   spy.assert_called_with(
      settled_tranaction["fees"][0]["to"]["id"], 
      {"balance":"{0}".format(-int(float(settled_tranaction["fees"][0]["fee"])))}
   )
   spy.assert_called_with(
      settled_tranaction["fees"][1]["to"]["id"], 
      {"balance":"{0}".format(-int(float(settled_tranaction["fees"][1]["fee"])))}
   )

In the function that we are trying to test the order of the calls are exactly as defined in the test (with different arguments). However, the test is failing with the following error:

>      spy.assert_called_with(to_node_id, update_transaction_payload)
E      AssertionError: Expected call: balance_update('6156661f7c1c6b71adefbb40', {'balance': '-10000', 'is_cma': False, 'currency': 'cUSD'})
E      Actual call: balance_update('559339aa86c273605ccd35df', {'balance': '5'})

Basically, it is asserting the last set of arguments.

What is the correct way to test something like that?

Tried this - didn't work either...



Solution 1:[1]

I created a pytest plugin to help me in those situations: pip install pytest-mock-generator

Once you install it, you'll have the mg fixture. You can put this line of code in your test and it would print and return the asserts for you: mg.generate_asserts(spy).

Here is a complete code example:

Say that you have a python file named example.py:

def hello(name: str) -> str:
    return f"Hello {name}!"

Then you have this test:

import example


def test_spy_list_of_calls(mocker, mg):
    example.hello("before spy")

    my_spy = mocker.spy(example, "hello")

    example.hello("after spy")
    example.hello("another after spy")
    example.hello("one more time")

    mg.generate_asserts(my_spy)

The final line would print this:

from mock import call

assert 3 == my_spy.call_count
my_spy.assert_has_calls(
    calls=[call('after spy'), call('another after spy'), call('one more time'), ])

Add those lines to your test and it should work:

import example

from mock import call


def test_spy_list_of_calls(mocker):
    example.hello("before spy")

    my_spy = mocker.spy(example, "hello")

    example.hello("after spy")
    example.hello("another after spy")
    example.hello("one more time")

    assert 3 == my_spy.call_count
    my_spy.assert_has_calls(
        calls=[call('after spy'), call('another after spy'), call('one more time'), ])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Peter K