我如何对这样的代码进行单元测试?

时间:2016-03-12 13:42:38

标签: python unit-testing testing oauth-2.0 pytest

我有以下类,目前正通过运行调用此文件中的方法的文件来测试它。然后我使用混合的print语句并检查博客以确保代码有效。

我真的想为此编写一些pytest单元测试并将其全部自动化,但我该怎么做呢?此外,如果身份验证不存在或已变为无效,则会打开浏览器并提示用户输入身份验证代码。稍后将由gui表格处理。 Pytest不接受用户输入,这是正确的;它不会自动化。

class BloggerInterface(object):
    """Connects to blogger api and authorises client."""

    def get_credentials(self):
        """Gets google api credentials, or generates new credentials
        if they don't exist or are invalid."""
        scope = 'https://www.googleapis.com/auth/blogger'

        flow = oauth2client.client.flow_from_clientsecrets(
                'client_secret.json', scope,
                redirect_uri='urn:ietf:wg:oauth:2.0:oob')

        storage = oauth2client.file.Storage('credentials.dat')
        credentials = storage.get()

        if not credentials or credentials.invalid:
            auth_uri = flow.step1_get_authorize_url()
            webbrowser.open(auth_uri)

            auth_code = input('Enter the auth code: ')
            credentials = flow.step2_exchange(auth_code)

            storage.put(credentials)

        return credentials

    def get_service(self):
        """Returns an authorised blogger api service."""
        credentials = self.get_credentials()
        http = httplib2.Http()
        http = credentials.authorize(http)
        service = apiclient.discovery.build('blogger', 'v3', http=http)

        return service

    def get_blog(self, blog_id):
        """Gets the details ofthe blog withthe id blog_id"""
        BlogDetails = collections.namedtuple('BlogDetails', 'blog_id, name, desc, url')

        conn = self.get_service()
        request = conn.blogs().get(blogId=blog_id, view='ADMIN')
        response = request.execute()

        name = response.get('name')
        desc = response.get('description')
        url = response.get('url')

        blog = BlogDetails(blog_id=blog_id, name=name, desc=desc, url=url)

        return blog

    def get_posts(self, blog_id, status='live'):
        """Gets all posts from the blog with the id blog_id"""
        posts = []

        conn = self.get_service()
        request = conn.posts().list(blogId=blog_id, view='ADMIN',
        status=status)

        #Responses are paginated, so a paging loop is required.
        while request:

            response = request.execute()

            for post in response.get('items', []):
                post_id = post.get('id')
                title = post.get('title')
                url = post.get('url')
                status = post.get('status')
                content = post.get('content')

                posts.append({'post_id':post_id, 'title':title, 'url':url,
                    'status':status, 'content':content})

            request = conn.posts().list_next(request, response)

        return posts

    def add_post(self, blog_id, post, is_draft=True):
        """Adds a new post to the blog with the id blog_id"""
        conn = self.get_service()

        #post is in the form {title, content, (labels), author_name, author_id.
        title, content, author_name, author_id, labels = post

        data = {
                'kind': 'blogger#post',
                'title': title,
                'content': content,
                'labels': labels,
                'author': {'displayName':author_name, 'id':author_id}
                }

        request = conn.posts().insert(blogId=blog_id, body=data,
                isDraft=is_draft)
        response = request.execute()
        post_id = response.get('id')

        return post_id

1 个答案:

答案 0 :(得分:4)

不要测试oauth2clientwebbrowser个项目。测试您的代码对其他部分的输入和输出的反应。这些是黑盒子,你可以用你自己的模拟替换它们,这样你就可以看到你的代码如何响应不同的返回值。

使用unittest.mock module制作模拟效果。如果您使用的是Python< 3.3,安装backport mock project即可。

例如,对于BloggerInterface.get_credentials(),您可以模拟oauth2client.client.flow_from_clientsecrets()oauth2client.file.Storage()webbrowser.open()input。然后,您可以使用storage.get()的响应强制您的代码使用webbrowser.open(),并测试您的代码是否正确尝试打开W​​eb浏览器,然后调用storage.put()来存储凭据:

with mock.patch('oauth2client.client.flow_from_clientsecrets') as mockflow, \
        mock.patch('oauth2client.file.Storage') as MockStorage, \
        mock.patch('webbrowser.open') as mockwbopen, \
        mock.patch('yourmodule.input') as mockinput:
    # set the credentials to invalid
    storage = MockStorage.return_value
    credentials = storage.get.return_value
    credentials.invalid = True

    # run the method and see if we get what we want
    result = BloggerInterface().get_credentials()

    # check that the flow was initialised correctly
    mockflow.assert_called_with(
        'client_secret.json', 'https://www.googleapis.com/auth/blogger',
        redirect_uri='urn:ietf:wg:oauth:2.0:oob')
    MockStorage.assert_called_with('credentials.dat')

    # With invalid credentials, the code should obtain a auth url from the
    # flow, pass it to the browser. Then the authentication code should be taken
    # from input and passed back to the flow for exchange. Test these
    # interactions took place:
    flow.step1_get_authorize_url.assert_called_once_with()
    mockwbopen.assert_called_once_with(flow.step1_get_authorize_url.return_value)
    flow.step2_exchange.assert_called_once_with(mockinput.return_value)
    storage.put(flow.step2_exchange.return_value)
    assert result == flow.step2_exchange.return_value