Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a function in python which is called in the global scope of a module using Pytest?

I have written the following function in "sample.py" and corresponding test script in "test_sample.py". The main function roll_dice is in module named "dice.py". All these files are in a folder called "myapp".

Scenario 1

dice.py

import random
def roll_dice():
    print("rolling...")
    return random.randint(1, 6)

sample.py

from myapp.dice import roll_dice
def guess_number(num):
    result = roll_dice()
    if result == num:
        return "You won!"
    else:
        return "You lost!"

test_sample.py

@mock.patch("myapp.sample.roll_dice")
def test_guess_number(mock_roll_dice):
    mock_roll_dice.return_value = 3
    assert guess_number(3) == "You won!"

When I run the test using Pytest, it runs successfully. But when I make a small change in sample.py shown below, the test fails:

Scenario 2

sample.py

from myapp.dice import roll_dice
result = roll_dice() # Here is the change
def guess_number(num):
    if result == num:
        return "You won!"
    else:
        return "You lost!"

Rest everything remains same!

Instead of calling function inside another function, when I call it in the global scope of sample.py module, test fails. Can someone tell me how to mock the roll_dice function in Scenario 2?

My guess is that we can not mock function calls in global scope of a module. Is that correct?

like image 709
Harshvardhan Palawat Avatar asked Oct 26 '25 12:10

Harshvardhan Palawat


1 Answers

In the scenario 2, roll_dice is call at import time. Complete test_sample.py should be:

from myapp.sample import guess_number      # Call to `roll_dice` is done here
                                           # before mock

@mock.patch("myapp.sample.roll_dice")
def test_guess_number(mock_roll_dice):
    mock_roll_dice.return_value = 3
    assert guess_number(3) == "You won!"

If you move import inside test function, it don't work too, because patch("myapp.sample.roll_dice") will import myapp.sample before mocking.

It will work with:

@mock.patch("myapp.dice.roll_dice")
def test_guess_number(mock_roll_dice):
    from myapp.sample import guess_number  # Call to `roll_dice` is done here
                                           # You shouldn't have imported `myapp.sample`
                                           # before
    mock_roll_dice.return_value = 3
    assert guess_number(3) == "You won!"

But be careful with code executed at import time (global variable initialization). At final the only thing you test is your knowledge of the mock library.

like image 178
Balaïtous Avatar answered Oct 28 '25 03:10

Balaïtous



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!