zoukankan      html  css  js  c++  java
  • django入门-测试-part5

    尊重作者的劳动,转载请注明作者及原文地址 http://www.cnblogs.com/txwsqk/p/6515996.html 

    完全翻译自官方文档 https://docs.djangoproject.com/en/1.10/intro/tutorial05/

    前面好几段文字都是介绍什么是自动化测试,我们为什么需要写测试用例,还有现在流行的"测试驱动开发",总之有如下好处:

    • 节省你的时间
      你修改了工程,设计好多模块,如果没有自动化测试,好吧,手动一个个测吧
    • 测试不能发现问题,但是能阻止问题的发生
      没有测试用例,那么你程序的行为就是不可预见的,即使这是你写的代码,你实际也不知道它内部是怎么运行的;有了测试用例,当某一个地方出问题,它就能指出这部分有问题,即使你自己压根没有意识到这里有问题
    • 测试可以让你的代码更有吸引力
      当你写了一段很牛[A-Z][1]的代码,但是别人并不想看,因为你没有测试用例,那么别人就认为你的代码是不可信的
    • 测试让团队合作更愉快
      小的应用可能只有一个开发者,但是大部分复杂的应用是一个团队一起开发的,测试用例可以防止你的同事不小心干扰了你的代码

    好了,总之就是为你的工程写测试 百利无一害 

    在前面章节中我们是怎么验证代码的,通过django-admin的shell

    >>> import datetime
    >>> from django.utils import timezone
    >>> from polls.models import Question
    >>> # create a Question instance with pub_date 30 days in the future
    >>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
    >>> # was it published recently?
    >>> future_question.was_published_recently()
    True

    好了下面我们用django自带的测试功能--自动化测试

    在你的应用目录下有一个 test.py  就是它

    django的自动化测试系统会在你的应用目录下找test开头的文件然后运行它

    我们往test.py里写个测试用例

    import datetime
    
    from django.utils import timezone
    from django.test import TestCase
    
    from .models import Question
    
    
    class QuestionMethodTests(TestCase):
    
        def test_was_published_recently_with_future_question(self):
            """
            was_published_recently() should return False for questions whose
            pub_date is in the future.
            """
            time = timezone.now() + datetime.timedelta(days=30)
            future_question = Question(pub_date=time)
            self.assertIs(future_question.was_published_recently(), False)

    运行一下

    python manage.py test polls

    运行这个命令都发生了什么呢

    这个类继承了TestCase, 它会创建一个测试用的数据库,这个类的方法都应该是test开头的方法

    运行结果如下

    Creating test database for alias 'default'...
    F
    ======================================================================
    FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMethodTests)
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "/path/to/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_question
        self.assertIs(future_question.was_published_recently(), False)
    AssertionError: True is not False
    
    ----------------------------------------------------------------------
    Ran 1 test in 0.001s
    
    FAILED (failures=1)
    Destroying test database for alias 'default'...

    显然测试失败了

    我们创建了一个问题,这个问题的发布时间是30天后,那么我们显然希望was_published_recently()返回False

    但是测试用例却返回了True,所以我们代码有bug,我们修复一下polls/models.py

    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now

    在重新执行下 

    python manage.py test polls

    好了这回测试通过了

    Creating test database for alias 'default'...
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.001s
    
    OK
    Destroying test database for alias 'default'...

    现在我们修复了 was_published_recently()的bug,我们来加几个复杂的测试用例

    def test_was_published_recently_with_old_question(self):
        """
        was_published_recently() should return False for questions whose
        pub_date is older than 1 day.
        """
        time = timezone.now() - datetime.timedelta(days=30)
        old_question = Question(pub_date=time)
        self.assertIs(old_question.was_published_recently(), False)
    
    def test_was_published_recently_with_recent_question(self):
        """
        was_published_recently() should return True for questions whose
        pub_date is within the last day.
        """
        time = timezone.now() - datetime.timedelta(hours=1)
        recent_question = Question(pub_date=time)
        self.assertIs(recent_question.was_published_recently(), True)

    下面我们来测试视图函数

    我们在创建question时,如果pub_date是未来的时间,那么按理说这不应该在页面显示的,就像京东明天12:00有抢购活动,那么在这个时间点之前是不应该让用户看到的

    在继续讲之前,先介绍一个django的测试命令 django.test.Client, 它可以在视图层模拟用户的交互行为,我们可以用Client命令在test.py或shell中

    >>> from django.test.utils import setup_test_environment
    >>> setup_test_environment()

    这个命令会加载模板渲染功能,这样我们就可以检验一些额外的属性比如response.context

    注意这个命令不会去创建测试数据库,它用真实的数据库数据做测试

    还有不要忘了在settings.py里设置正确的时区TIME_ZONE

    Client怎么用呢

    >>> from django.test import Client
    >>> # create an instance of the client for our use
    >>> client = Client()
    >>> # get a response from '/' >>> response = client.get('/') >>> # we should expect a 404 from that address >>> response.status_code 404 >>> # on the other hand we should expect to find something at '/polls/' >>> # we'll use 'reverse()' rather than a hardcoded URL >>> from django.urls import reverse >>> response = client.get(reverse('polls:index')) >>> response.status_code 200 >>> response.content b' <ul> <li><a href="/polls/1/">What&#39;s up?</a></li> </ul> ' >>> # If the following doesn't work, you probably omitted the call to >>> # setup_test_environment() described above >>> response.context['latest_question_list'] <QuerySet [<Question: What's up?>]>

    好了回到正题,修复我们的index视图,之前的index视图是这样的,没有判断question的发布时间是否是小于现在的时间

    class IndexView(generic.ListView):
        template_name = 'polls/index.html'
        context_object_name = 'latest_question_list'
    
        def get_queryset(self):
            """Return the last five published questions."""
            return Question.objects.order_by('-pub_date')[:5]

    修正这个问题,当pub_date小于现在的时间时不显示

    def get_queryset(self):
        """
        Return the last five published questions (not including those set to be
        published in the future).
        """
        return Question.objects.filter(
            pub_date__lte=timezone.now()
        ).order_by('-pub_date')[:5]

    来测试一下这个新视图吧

    def create_question(question_text, days):
        """
        Creates a question with the given `question_text` and published the
        given number of `days` offset to now (negative for questions published
        in the past, positive for questions that have yet to be published).
        """
        time = timezone.now() + datetime.timedelta(days=days)
        return Question.objects.create(question_text=question_text, pub_date=time)
    
    
    class QuestionViewTests(TestCase):
        def test_index_view_with_no_questions(self):
            """
            If no questions exist, an appropriate message should be displayed.
            """
            response = self.client.get(reverse('polls:index'))
            self.assertEqual(response.status_code, 200)
            self.assertContains(response, "No polls are available.")
            self.assertQuerysetEqual(response.context['latest_question_list'], [])
    
        def test_index_view_with_a_past_question(self):
            """
            Questions with a pub_date in the past should be displayed on the
            index page.
            """
            create_question(question_text="Past question.", days=-30)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question.>']
            )
    
        def test_index_view_with_a_future_question(self):
            """
            Questions with a pub_date in the future should not be displayed on
            the index page.
            """
            create_question(question_text="Future question.", days=30)
            response = self.client.get(reverse('polls:index'))
            self.assertContains(response, "No polls are available.")
            self.assertQuerysetEqual(response.context['latest_question_list'], [])
    
        def test_index_view_with_future_question_and_past_question(self):
            """
            Even if both past and future questions exist, only past questions
            should be displayed.
            """
            create_question(question_text="Past question.", days=-30)
            create_question(question_text="Future question.", days=30)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question.>']
            )
    
        def test_index_view_with_two_past_questions(self):
            """
            The questions index page may display multiple questions.
            """
            create_question(question_text="Past question 1.", days=-30)
            create_question(question_text="Past question 2.", days=-5)
            response = self.client.get(reverse('polls:index'))
            self.assertQuerysetEqual(
                response.context['latest_question_list'],
                ['<Question: Past question 2.>', '<Question: Past question 1.>']
            )

    讲解:

    create_question()函数让我们方便的创建question

    上面的几个函数根据函数名就能知道是干什么的,不解释了

    index视图修复完了,detail视图也得改

    class DetailView(generic.DetailView):
        ...
        def get_queryset(self):
            """
            Excludes any questions that aren't published yet.
            """
            return Question.objects.filter(pub_date__lte=timezone.now())

    一样的逻辑,当pub_date less than equeal 小于等于timezone.now()现在的时间则不显示

    为detail写测试用例

    class QuestionIndexDetailTests(TestCase):
        def test_detail_view_with_a_future_question(self):
            """
            The detail view of a question with a pub_date in the future should
            return a 404 not found.
            """
            future_question = create_question(question_text='Future question.', days=5)
            url = reverse('polls:detail', args=(future_question.id,))
            response = self.client.get(url)
            self.assertEqual(response.status_code, 404)
    
        def test_detail_view_with_a_past_question(self):
            """
            The detail view of a question with a pub_date in the past should
            display the question's text.
            """
            past_question = create_question(question_text='Past Question.', days=-5)
            url = reverse('polls:detail', args=(past_question.id,))
            response = self.client.get(url)
            self.assertContains(response, past_question.question_text)

    继续总结

    你会发现当你开始写测试用例,你的测试代码就疯狂的增加了,而且他们很多重复的内容,相比你写的代码,测试代码是那么的丑陋,这不要紧,因为大部分时间你都不会察觉到他们的存在,你可以继续你的开发任务,不过当你修改了视图逻辑,你也要同步更新你的测试用例,不然会有一大顿测试用例过不了

    关于写测试用例,下面有几条好的建议:

    • 一个模型或视图一个单独的测试类
    • 每个测试的方法只测试一种情况
    • 测试方法的名字要顾名思义

    扩展一下

    我们现在只介绍了基本的测试功能,当你想测试浏览器的行为时可以使用 "Selenium"测试框架,这个框架不光能测试你的代码,还有你的javascript,django也包含了一个工具LiveServerTestCase来让你使用Selenium

    当你的功能很复杂时,你可能希望当你提交代码时能自动运行测试,那么你去关注一下"持续集成"相关的内容

    现在很多ide可以检查你的代码覆盖率,用这个方法也能找出你的哪些代码还没有被测试到,这也能让你发现哪些代码已经没用了可以删掉了.

    如果你发现有的代码无法被测试,很明显这段代码应该被重构或者删除.有个工具Coverage可以帮你识别无用的代码 参考https://docs.djangoproject.com/en/1.10/topics/testing/advanced/#topics-testing-code-coverage

  • 相关阅读:
    lua面向对象(定义与调用)
    luastring(字符串)
    luatable(表)
    lua面向对象(创建与实例化)
    pandas安装方法(常规安装失败解决方法)
    lua循环
    windows常用命令schtasks
    ios UI自动化 appium参数配置
    ios UI自动化环境配置
    jmeter进行websocket 通信
  • 原文地址:https://www.cnblogs.com/txwsqk/p/6515996.html
Copyright © 2011-2022 走看看