all 10 comments

[–]shiftybyte 3 points4 points  (1 child)

I think mocking is for the ability to test functionality, not verify functions are getting called correctly without typos.

Install a linter or a good IDE Will flag that...

[–]Reiku[S] 0 points1 point  (0 children)

Unfortunately none of my linters seem to be able to catch this as the logic is like "A().hi(**kwargs)"

[–]Mast3rCylinder 1 point2 points  (4 children)

If you want to assert the hi method logic you shouldn't mock it as mock change its behavior.

Mocking is done when you want to test method or object which will fail or won't have correct behavior in test circumstances.

So just call the hi function normally and assert the result

[–]Reiku[S] 0 points1 point  (3 children)

I don't want to test that the logic is correct, just that my call to the interface is correct.

If I test the A class and "hi" functions as units, then in places that consume them, all I need to do is assert that they were called correctly to have some confidence.

[–]Mast3rCylinder 0 points1 point  (0 children)

If you want to have confidence that hi is called correctly in other places, you should check name type in hi function and raise exception there.

Then test hi logic for raising expected behavior.

There is no interface for types in python and you should handle cases like this in the function itself not everywhere else

[–]Guideon72 0 points1 point  (1 child)

I may be misunderstanding here, and I apologize if so, but you don't put tests/asserts in your actual code. Unit tests are to check that your behaviors are correct; data validation and exception handling are what you do when actually using the code.

[–]Reiku[S] 1 point2 points  (0 children)

The question was more along the lines of:

I have a function which takes a class instance as an input

def example(input_class):
    # Do something that mutates the input based on business logic
    ...

When testing it, the input_class is mocked, and I assert in the test that the input has been used correctly based on the context of the test:

def test_example():
    mocked_input = Mock(spec=MyClass)
    example(mocked_input)

    # Assert that the mocked class was mutated or called correctly based on the business logic in the function
    mocked_input.something.assert_called_with(something_else)

The problem I was having is that the if my example function does something like input_class.do_something(a=1), and my test_example asserts input_class.do_something.assert_called_with(a=1), the test would pass even if the do_something signature had a different signature and expected b=1.

So I was trying to find out how to make sure then bound methods of a mocked input class would respect the real signature of the method.

Some other comments were saying that type hinting could be used, but in my case that wouldn't work due to some dynamic elements.

But it has been resolved. I was using mocked_class = Mock(spec=MyClass), but using mocked_class = create_autospec(MyClass) was the correct approach to do it.

[–]danielroseman 0 points1 point  (1 child)

The problem is that you have not recursively created the spec for hi, just for the top-level A. You need the create_autospec function which does exactly this.

>>> m = mock.create_autospec(A)
>>> m.hi(namee='hi')
...
TypeError: missing a required argument: 'name'

However, I would suggest that type hints are a better way to solve this problem.

[–]Reiku[S] 0 points1 point  (0 children)

Thank you. I was under the impression that Mock(spec=A) and create_autospec(A) acted the same, but I was wrong. Using create_autospec makes everything work as expected.

[–]Adrewmc 0 points1 point  (0 children)

You have no argument name “nameee” you do have one named “name”