Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to move the mock.patch to the setUp?

I have the following two unit tests:

    @mock.patch('news.resources.generator.Generator.get_header')
    @mock.patch('news.scraper.bbc_spider.BBCSpider.save_scraped_rss_into_news_model')
    @mock.patch('news.scraper.bbc_spider.BBCSpider.get_news_urls')
    @mock.patch('requests.get')
    def test_get_header_is_called(self, req_get, spi_news, spi_save_rss, get_head):
        spi_news.return_value = {'x': 1}
        gen = Generator()
        gen.get()
        get_head.assert_called_with()

    @mock.patch('news.resources.generator.Generator.get_header')
    @mock.patch('news.scraper.bbc_spider.BBCSpider.save_scraped_rss_into_news_model')
    @mock.patch('news.scraper.bbc_spider.BBCSpider.get_news_urls')
    @mock.patch('requests.get')
    def test_task_url_is_save_scraped_rss_into_news_model(self, req_get, spi_news, spi_save_rss, get_head):
        spi_news.return_value = {'x': 1}
        gen = Generator()
        gen.get()
        tasks = self.taskqueue_stub.GetTasks("newstasks")
        self.assertEqual(tasks[0]['url'], '/v1/worker/save-scraped-rss-into-news-model')

As you can see there is a lot of code repetition. Is there a way I could move the mock.patch to the setUp()?

Class TestGenerator(TestBase):
    def setUp(self):
        super(TestGenerator, self).setUp()
        mock.patch() ???

    def test_get_header_is_called(self, req_get, spi_news, spi_save_rss, get_head):
            spi_news.return_value = {'x': 1}
            gen = Generator()
            gen.get()
            get_head.assert_called_with()

    def test_task_url_is_save_scraped_rss_into_news_model(self, req_get, spi_news, spi_save_rss, get_head):
            spi_news.return_value = {'x': 1}
            gen = Generator()
            gen.get()
            tasks = self.taskqueue_stub.GetTasks("newstasks")
            self.assertEqual(tasks[0]['url'], '/v1/worker/save-scraped-rss-into-news-model')
like image 513
Houman Avatar asked Jan 10 '23 00:01

Houman


1 Answers

You have 2 options here -- First, you could move the patching to the class:

@mock.patch('news.resources.generator.Generator.get_header')
@mock.patch('news.scraper.bbc_spider.BBCSpider.save_scraped_rss_into_news_model')
@mock.patch('news.scraper.bbc_spider.BBCSpider.get_news_urls')
@mock.patch('requests.get')
class TestGenerator(TestBase):
    def test_get_header_is_called(self, req_get, spi_news, spi_save_rss, get_head):
      pass

when you use something in the mock.patch family on the class, it behaves as if you had patched each method that starts with "test"1 individually.

Your other option is to start the patches in setup. In a fictitious example (to save typing), it looks like this:

class SomeTest(TestCase):
    def setUp(self):
        super(SomeTest, self).setUp()
        patch = mock.patch('foo.bar.baz')
        mock_baz = patch.start()  # may want to keep a reference to this if you need to do per-test configuration
        self.addCleanup(patch.stop)

addCleanup was added in python2.7 (and is fantastic!). If you don't need older versions of python2.x, you should use it as it is more robust than the alternatives. The easiest alternative is to just stop all patches in tearDown:

class SomeTest(TestCase):
    def setUp(self):
        super(SomeTest, self).setUp()
        patch = mock.patch('foo.bar.baz')
        mock_baz = patch.start()  # may want to keep a reference to this if you need to do per-test configuration

    def tearDown(self):
        super(SomeTest, self).tearDown()
        mock.patch.stopall()

but you can also keep references to individual patches self.patch1 = mock.patch(...) and then stop that one individually in tearDown as necessary.

1Actually, mock.TEST_PREFIX which defaults to "test"

like image 129
mgilson Avatar answered Jan 16 '23 20:01

mgilson