如何模拟 YouTube 构建对象,YouTube 查询的搜索结果

问题描述 投票:0回答:1

我正在尝试将单元测试添加到我的 python 项目中,但在尝试模拟我的 YouTube 构建对象时遇到了困难。我在模拟时遇到麻烦的变量是

results
变量。

模拟对象被传递到我的

app.py
中的结果变量,但是每当下一行尝试运行时,

video_id = results['items'][0]['id']['videoId']
,

我在终端中收到此错误:

video_id = results['items'][0]['id']['videoId'] TypeError: 'Mock' object is not subscriptable

我尝试过的解决方案:

  1. @patch('app.youtube.search.list.execute')
    错误:
    AttributeError: 'function' object has no attribute 'list'

  2. mock_youtube_search.return_value.list.execute.return_value = response_data
    这不会返回任何错误并呈现页面,但
    video_id
    变量都是错误的。 video_id 变量出现在嵌入 URL 的末尾,https://www.youtube.com/embed/。下面,我展示了
    <iframe>
    标签中 video_id 变量的值。

这是当我使用我尝试过的

解决方案2
打印html时显示的<iframe>标签:

<iframe src="https://www.youtube.com/embed/<MagicMock name=\'search().list().execute().__getitem__().__getitem__().__getitem__().__getitem__()\' id=\'2662986714704\'>"></iframe>

这是我对

app.py
文件中索引路线的 POST 请求。

    elif request.method == "POST":
        # Get movie info from movie_id that was submitted in the form
        movie = db.execute("SELECT * FROM movies WHERE id = ?", request.form.get("movie_id"))[0]

        # Search youtube build object and get back a list, passing in movie title the user clicked and set number of results = 1, set parameter part to snippet
        results = youtube.search().list(part='snippet', q=f'{movie["title"]} trailer', maxResults=1).execute()

        # Find video ID of the results query
        video_id = results['items'][0]['id']['videoId']

        # Now that we have YouTube video ID, use embed URL to link to the video in the iFrame tag
        movie_link = f'https://www.youtube.com/embed/{video_id}'

        return render_template("description.html", movie = movie, URL = large_poster_path_URL, movie_link = movie_link)

这是我的

app_test.py
文件中的代码。

    @patch('app.youtube.search')
    @patch('app.db.execute')
    def test_index_route_POST(self, mock_db_execute, mock_youtube_search):
        # Make sure user is logged in
        with self.app.session_transaction() as session:
            session['user_id'] = 1
            session['logged_in'] = True

        # Mock database SELECT query
        mock_db_execute.return_value = [
            {'id': 2, 
             'title': 'Teenage Mutant Ninja Turtles: Mutant Mayhem', 
             'overview': "After years of being sheltered from the human world, the Turtle brothers set out to win the hearts of New Yorkers and be accepted as normal teenagers through heroic acts. Their new friend April O'Neil helps them take on a mysterious crime syndicate, but they soon get in over their heads when an army of mutants is unleashed upon them.", 
             'release_date': '2023-07-31', 
             'poster_path': '/poster_path_3.jpg'}
        ]

        # Mock the YouTube API response using custom mock
        response_data = Mock(return_value={"items": [{"id": {"kind":"youtube#video","videoId":"IHvzw4Ibuho"}}]})
        print("This is the response data: ", response_data())
        mock_youtube_search.return_value = response_data

        # POST request to index route
        response = self.app.post('/', follow_redirects=True)
        print(response.data) # Debugging
       
        # Assert that the response status code is 200 (OK)
        self.assertEqual(response.status_code, 200)

        # Assert that index.html is showing correct database information
        soup = BeautifulSoup(response.data, 'html.parser')
        movie_title = soup.find('p')
        movie_title_text = movie_title.text.strip()
        self.assertEqual(movie_title_text, 'Movie title: Teenage Mutant Ninja Turtles: Mutant Mayhem')
        

我哪里搞砸了?

python mocking python-unittest google-api-python-client
1个回答
0
投票

TLDR;这是我的解决方案: 将

mock_youtube_search.return_value = response_data
替换为
mock_youtube_search().list.return_value.execute= response_data

我想通了!来自 google-python-api-client 的 YouTube 构建对象很难理解。所以,我的 Mock() 数据是正确的,并且格式正确。该问题源于每当我返回时都没有将该返回值:

{"items": [{"id": {"kind":"youtube#video","videoId":"IHvzw4Ibuho"}}]}
放置在正确的位置。

看看这行代码,

mock_youtube_search.return_value = response_data
,以及这个补丁装饰器,
@patch('app.youtube.search')
,在我上面帖子中的
app_test.py
片段中。

我正在嘲笑我的

app.py
中的结果变量:

results = youtube.search().list(part='snippet', q=f'{movie["title"]} trailer', maxResults=1).execute()

本质上,我只是为 youtube.search() 部分创建返回值,而不是整个语句。我想要获得

execute()
部分的模拟返回值,这是一个嵌套调用。因此,我必须尝试并使用一些逻辑来获得
.return_value
的正确顺序以及放置它的位置。由于我修补了
youtube.search()
部分,因此我必须像这样调用
mock_youtube_search
mock_youtube_search()
。然后,我必须将其添加到它的末尾,
.list.return_value.execute

总结:调用与补丁装饰器相对应的模拟对象,添加

.list.return_value
,因为
.list()
在 YouTube 构建对象中返回一个值,然后添加
.execute
,因为这就是您想要实际模拟的内容,将整个语句组合起来,
mock_youtube_search().list.return_value.execute
等于
response_data

我将数据打印出来,以便您可以看到它正确打印。

原始输出。

© www.soinside.com 2019 - 2024. All rights reserved.