TDD(测试驱动开发)

【django】TDD测试驱动开发

2018-05-21  本文已影响2人  MarcoHorse

测试驱动开发是python web开发里面一个很重要的角色!使用测试来推动开发的进程,通过测试用例的编写,对需求功能分解,使用过程和接口都进行了设计,而tdd里面的测试代码用例是对自己的代码最好的解释。测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug

下面简单讲下python里面用到的三种单元测试框架

  1. unitest
  2. django.test
  3. rest-framework.test
  4. 如何编写单元测试

1. unitest.TestCase 系统提供的测试工具

unittest模块提供了一组丰富的工具来构建和运行测试。下面演示了这些工具的一小部分足以满足大多数用户的需求。
下面是一个测试三种字符串方法的简短脚本:

import unittest

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)

if __name__ == '__main__':
    unittest.main()

testcase是由子类化unittest.testcase创建的。这三个单独的测试是用名字以字母测试开始的方法定义的。这种命名约定通知测试运行者关于哪些方法表示测试。每个测试的关键是对断言()的调用,以检查预期的结果;断言()或断言错误()来验证一个条件;或者断言()来验证一个特定的异常是否被提高。这些方法被用来代替assert语句,这样测试运行者就可以累积所有的测试结果并生成报告。

也可以使用以下两句话来代替上面代码的最后两行

suite = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
unittest.TextTestRunner(verbosity=2).run(suite)

2. django.testTestCase django框架提供的测试框架

2.1 如何编写django的测试框架
django.test依赖于python的unitest框架,testcase的测试方法依赖于类的方法定义测试

from django.test import TestCase
from myapp.models import Animal

class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

2.2 如何运行django的测试框架
当您运行测试时,测试实用程序的默认行为是在任何以test开头的文件中找到所有的测试用例(即unittest.testcase的子类),自动地从这些测试用例中构建一个测试套件,并运行该套件。您可以通过向。/manage提供任意数量的“测试标签”来指定要运行的特定测试。py测试。每个测试标签都可以是一个完整的Python点路径到一个包、模块、TestCase子类或测试方法。

# Run all the tests in the animals.tests module
$ ./manage.py test animals.tests

# Run all the tests found within the 'animals' package
$ ./manage.py test animals

# Run just one test case
$ ./manage.py test animals.tests.AnimalTestCase

# Run just one test method
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak

如果您在测试运行时按Ctrl-C,那么测试运行程序将等待当前运行的测试完成,然后优雅地退出。在一个优雅的退出过程中,测试运行程序将输出任何测试失败的详细信息,报告运行了多少测试以及遇到了多少错误和故障,并像往常一样销毁任何测试数据库。因此,如果您忘记传递-failfast选项,那么按Ctrl-C是非常有用的,注意一些测试出乎意料地失败,并且希望在不等待完整测试运行完成的情况下获得失败的详细信息。

3. rest-framework.test基于restframework的api测试

rft的创建测试请求和django的一致,所以其也可以实现self.get|post等系列请求

class ProductViewTest(TestCassMixin, APITestCase):
    base_url = '/store/product'
    model = Product
    serializer_class = ProductSerializer

    def test_list_filter(self):
        """列表筛选功能"""
        url = f"{self.base_url}/"
        query_keys = ("brand", "type")
        foreign_keys = ("brand", )

        for key in query_keys:
            if key in foreign_keys:
                val = getattr(self.product, key + "_id")
            else:
                val = getattr(self.product, key)

            # 查询条件匹配,返回数据 > 0
            response = self.client.get(url, data={key: val})
            self.assertEqual(response.status_code, 200)  # 接口响应正确
            self.assertGreater(response.data['count'], 0)

            # 查询条件不匹配,返回数据 = 0
            response = self.client.get(url, data={key: val + 1})
            self.assertEqual(response.status_code, 200)  # 接口响应正确
            self.assertEqual(response.data['count'], 0)

更多测试方式:http://www.django-rest-framework.org/api-guide/testing/

4. 如何编写单元测试

4.1 这个例子是测试myapp.models 中的 Animal 类相关的方法功能。

from django.test import TestCase
from myapp.models import Animal
 
 
class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")
 
    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

4.2 用代码访问网址的方法:

>>> from django.test import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
'<!DOCTYPE html...'

我们可以用 django.test.Client 的实例来实现 get 或 post 内容,检查一个网址返回的网页源代码。

默认情况下CSRF检查是被禁用的,如果测试需要,可以用下面的方法:

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

使用 csrf_client 这个实例进行请求即可。

指定浏览USER-AGENT:

>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')

模拟post上传附件:

from django.test import Client
c = Client()
 
with open('wishlist.doc') as fp:
    c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})

测试网页返回状态:

from django.test import TestCase
 
class SimpleTest(TestCase):
    def test_details(self):
        response = self.client.get('/customer/details/')
        self.assertEqual(response.status_code, 200)
 
    def test_index(self):
        response = self.client.get('/customer/index/')
        self.assertEqual(response.status_code, 200)

我们用 self.client 即可,不用 client = Client() 这样实例化,更方便,我们还可以继承 Client,添加一些其它方法:

from django.test import TestCase, Client
 
class MyTestClient(Client):
    # Specialized methods for your environment
    ...
     
class MyTest(TestCase):
    client_class = MyTestClient
 
    def test_my_stuff(self):
        # Here self.client is an instance of MyTestClient...
        call_some_test_code()

定制 self.client 的方法:

from django.test import Client, TestCase
 
 
class MyAppTests(TestCase):
    def setUp(self):
        super(MyAppTests, self).setUp()
        self.client = Client(enforce_csrf_checks=True)
 
    def test_home(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)
上一篇下一篇

猜你喜欢

热点阅读