1

Here is what I want to test:

test.py

class MyTest(TestCase)

    def test1(self):
        data = crawl_data()

        Check data lengh and current data in DB...

        function2()

        Again, Check data lengh and current data in DB...

And program code:

a.py

def crawl_data():
    """
    crawl data from web
    """
    .
    .
    return web_crawled_data

b.py

from a import crawl_data

def function2():
    """
    crawl data and store new data in database
    """
    .
    .
    result = crawl_data()
    .
    .
    update data and update Database
    .
    . 

What I want to do is testing function2(). Problem is though function2() call crawl_data() inside.

I don't want crawl_data() to crawl data from web in the test, So I tried mocking this function.

@patch("a.crawl_data")
def test1(self, crawl_data):
    crawl_data.return_value = [("112233", "ABCDEF"), ("222233", "EFGHIJ")]

    data = crawl_data()

    Check data length and current data in DB

    function2()

    Check the database whether new data stored in DB

When I run the test, function2() still crawl data from the web in real!

The reason that I don't want to mock function2 is that, when I start the test, test framework use virtual database (provided by django)!

So, What I want to do is make crawl_data() as a mock when the function2() call it inside.

How can I do that?





EDIT

I followed the solution "Martijn Pieters♦" gave, and implement code like below:

a.py

def sum():
    return 1

b.py

from a import sum

def calling_sum():
    return sum()

test1.py

@patch("b.sum")
def test_sum(self, sum):
    sum.return_value = 10
    print(calling_sum())

result

It print out "1", not 10....

I tried to change @patch("b.sum") to @patch("b.fake_sum") to check whether it import correctly, and @patch("b.fake_sum") occured error(something like fake_sum not exsits) so I think importing module works well

Still doesn't work..

user3595632
  • 5,380
  • 10
  • 55
  • 111
  • Your edit has both the original and the mock return 10. If you are seeing `1` then you are not running the code posted. – Martijn Pieters Feb 08 '18 at 10:38
  • @MartijnPieters I edited it! – user3595632 Feb 08 '18 at 10:40
  • What is `b()`? Please, post a proper MCVE, code you actually can copy and run. – Martijn Pieters Feb 08 '18 at 10:41
  • @MartijnPieters Really sorry about making such a mistake T_T – user3595632 Feb 08 '18 at 10:42
  • See https://gist.github.com/mjpieters/f88ee12a72f876f2b52a3983b58287d2 for what your test does. I renamed `a.py` to `foo.py`, and `b.py` to `ham.py` to create a more distinct difference. I also used `mock_foo_bar` as the name for the mock object to be clear that the argument passed in by `patch()` is a mock, not the original. The test shows that the mock is applied correctly. That's because the `ham.bar` reference is the global that `calling_foo_bar()` sees at the time it is called. Your MCVE **can't work differently**. It really does not reproduce. – Martijn Pieters Feb 08 '18 at 11:58

1 Answers1

1

You are patching the wrong reference to crawl_data; the b module has a direct reference to the function, which patching a.crawl_data won't touch. Patch it in b:

@patch("b.crawl_data")
def test1(self, crawl_data):
    crawl_data.return_value = [("112233", "ABCDEF"), ("222233", "EFGHIJ")]

Also see the Where to patch section of the mock module documentation.

There is no point in using data = crawl_data() in your test, there is little point in testing your mock.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks! but what is the meaning of "There is no point in using `data = crawl_data()` in your test, there is little point in testing your mock."? I'm not native english speaker, so hope explain in easy way please? – user3595632 Feb 07 '18 at 14:14
  • When I follow your advices and run test, mocked `crawled_data.called()` return `False` after calling `function2()` in the test code...and it still crawl data from web.. not mocked!! – user3595632 Feb 07 '18 at 15:03
  • @user3595632: in your question, you have `data = crawl_data()` in your `test1` method, right after the `crawl_data.return_value = [...]` assignment. Why do you call `crawl_data()` there? That's a call to the mock, and that call will always return the list you just assigned to `crawl_data.return_value`. That's not a useful test. – Martijn Pieters Feb 07 '18 at 15:23
  • @user3595632: for your `b.py` module, as shown in the question, `@patch("b.crawl_data")` **does work**, and for the duration of `test1()`, using the name `crawl_data()` in `function2()` **will** call the mock. Perhaps you have not accurately recreated your actual situation? – Martijn Pieters Feb 07 '18 at 15:25
  • @Martjin Pieters : I edited the post. Please check that out – user3595632 Feb 08 '18 at 01:03