1

Having some trouble understanding how to mock a class instance attribute. The class is defined by the package "newspaper3k", e.g.: from newspaper import Article.

I have been stuck on this for a while and I seem to be going nowhere even after looking at the documentation. Anyone can give me a pointer on this?

"""utils/summarizer.py module """

import nltk
from newspaper import Article


def generate_summary(url: str) -> str:
    article = Article(url)
    article.download()
    article.parse()

    try:
        nltk.data.find("tokenizers/punkt")
    except LookupError:
        nltk.download("punkt")
    finally:
        article.nlp()

    return article.summary

My current attempt looks like this... I am afraid I am confusing libraries and the way mocking is done in python with pytest.

from newspaper import Article

import app.utils.summarizer as summarizer


def test_generate_summary(mocker, monkeypatch):
    article = mocker.MagicMock(Article)
    type(article).summary = mocker.PropertyMock(return_value="abc")

    # methods = {
    #     'download.return_value': None,
    #     'parse.return_value': None,
    #     'nlp.return_value': None,
    # }
    # article_mock = mocker.MagicMock(summary="abc", **methods)
    # monkeypatch.setattr("newspaper.article.Article", article_mock)

    monkeypatch.setattr(Article, "download", lambda _: None)
    monkeypatch.setattr(Article, "parse", lambda _: None)
    monkeypatch.setattr(Article, "nlp", lambda _: None)

    monkeypatch.setattr(
        "nltk.data.find", lambda _: "https://test-url.com/resource?name=foo#bar"
    )

    summary = summarizer.generate_summary("https://test-url.com")

    assert summary == "abc"

Any tips? Also, is there anyway I can mock "download", "parse" and, "nlp" methods more easily?

4
  • Can you elaborate at what you are actually testing? You seem to mock away the whole functionality, so you are testing nothing in the end. Commented Feb 13, 2022 at 19:06
  • Hey @MrBeanBremen. Yes, I have noticed that already in the meantime, but I still wanted to understand this use case. I am doing this for learning purposes and, eventually, one day or the other, this question might be useful in a better scenario. Also do not know if it makes a difference, but "Article.summary" is not set on init; It is set by one of the above methods. Commented Feb 13, 2022 at 20:16
  • 1
    First, you should use mocker.patch to actually patch Article instead of just creating a mock that will not be used. Second, if you patch it, it will be replaced by a mock, and each call on the mock will return another mock, so there is no need to patch the methods. Try to read some tutorials or examples on mocking first, there are plenty. Commented Feb 13, 2022 at 20:48
  • I will take a look on those, thank you. Commented Feb 13, 2022 at 21:03

1 Answer 1

2

Following MrBean Bremen advice... After going through the documentation again, again, I learned quite a few important things. I also consumed a few tutorials, but ultimately, none of them solved my problem or at least were not, IMO, good at explaining what the hell I was doing.

I was able to mock class attributes and instance methods when all I wanted to was to mock an instance attribute. I also read many tutorials, which did not help me fully understand what I was doing either.

Eventually, after a desperate google search with a piece of my own code that should not yield any important results (i.e.: mocker.patch.object(Article, summary="abc", create=True)), I came across the best tutorial I found all around the web over the last week, which finally helped me connect the docs.

The final solution for own my question is (docstring includes the tutorial that helped me):

def test_generate_summary(mocker):
    """See comprehensive guide to pytest using pytest-mock lib:

        https://levelup.gitconnected.com/a-comprehensive-guide-to-pytest-3676f05df5a0
    """
    mock_article = mocker.patch("app.utils.summarizer.Article")
    mock_article().summary = "abc"

    mocker.patch("app.utils.summarizer.nltk.data.find", return_value=None)

    summary = summarizer.generate_summary("https://test-url.com")

    assert summary == "abc"
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.