在敏捷/BDD中使用Django Doctest的示例

6 投票
4 回答
1057 浏览
提问于 2025-04-15 17:02

我想学习如何以更灵活和行为驱动开发(BDD)的方式进行文档测试和单元测试。
我找到了一些看起来还不错的教程,但它们只是一些简单的介绍。
我真正想要的是一些用BDD风格开发的Django项目的源代码。

我不太明白的是,如何处理请求对象等等。
我遇到的情况是,我的应用已经上线,但在生产环境中的表现和我在开发环境中,甚至在生产服务器的Python命令行中看到的完全不同。
我希望一些文档测试能帮助我诊断这个问题,同时也能为先写测试的灵活开发流程打开大门。

具体来说,我想测试的代码是:

def match_pictures_with_products( queryset, number_of_images = 3):      
    products = []  
    i = 0    
    for product in queryset:  
       if i < ( number_of_images ):  
           image =  product.imagemain_set.all()[:1]  
           product.photo_url = image[0].photo.url  

       products.append(product)  
       i += 1  

    return products  

def index(request):  
    """returns the top 10 most clicked products"""     
    products = Product.objects.all()[:10]  
    products = match_pictures_with_products( products, 10)  .  
    return render_to_response('products/product_list.html', {'products': products}) 

我该如何创建一个文档测试,以确保index返回10个对象?
在生产服务器的命令行中,产品查询似乎工作得很好,但实际服务器根本没有返回任何产品。

4 个回答

1

你可以使用 django的测试客户端 来测试那些被设置的上下文变量:

>>> response = client.get('/foo/')
>>> response.context['name']
'Arthur'

你还可以检查响应代码,以确保页面返回的是成功的 200

1

你现在写的这个视图(view)不太好测试。你得去抓取HTML代码,看看你想要的内容是否存在,这样的话你测试的内容就太多了。更好的做法是重写你的视图,让它更容易测试。首先,可以把模板的名字变成一个参数,这样你就可以创建一个简单的测试模板:

def index(request, template_name='products/product_list.html'):  
    """returns the top 10 most clicked products"""     
    products = Product.objects.all()[:10]  
    products = match_pictures_with_products( products, 10)  .  
    return render_to_response(template_name, {'products': products})

然后你可以写一个简单的模板,只需要计算产品的数量:

{{ products.count }} 

确保这个模板返回“10”。

3

我之前也问过自己同样的问题。我发现 doctests 在像视图、模型方法和管理器这些地方的用处有限,因为:

  1. 你需要能够设置和清理测试数据集,才能真正进行测试。
  2. 视图需要接收一个请求对象。在 doctest 中,这个对象从哪里来呢?

因此,我一直使用 Django 的 单元测试框架,它可以帮你处理这些问题。不过,遗憾的是,这样你就无法享受到 doctests 的一些好处,而且会让测试驱动开发(TDD)和行为驱动开发(BDD)变得更难。接下来是一些纯粹的猜测,关于你可能如何让这件事情运作:

我觉得你可以从各自的模块和函数中提取 doctests,并在单元测试框架中执行它们。这样就可以处理测试数据的设置和清理。如果你的 doctests 是在 Django 的 unittest.TestCase 子类的测试方法中执行的,它们就能使用那个测试数据库。你还可以将一个模拟的请求对象传入到 doc test 的执行环境中。这是一个 Django 代码片段,提供了一个模拟请求对象,以及 相关信息。假设你想测试一个应用中所有视图的文档字符串,你可以在 tests.py 中这样做:

from ??? import RequestFactory
from doctest import testmod, DocTestFailure
from django.test import TestCase

from myapp import views

class MyAppTest(TestCase):

    fixtures = ['test_data.json']

    def test_doctests(self):                
        try:
            testmod(views, extraglobs={
                'REQUEST': RequestFactory()
            }, raise_on_error=True)
        except DocTestFailure, e:
            self.fail(e)

应该允许你做到类似这样的事情:

def index(request):  
    """
    returns the top 10 most clicked products

    >>> response = index(REQUEST)
    >>> [test response content here]

    """     
    products = Product.objects.all()[:10]  
    products = match_pictures_with_products( products, 10)  .  
    return render_to_response('products/product_list.html', {'products': products})

再说一次,这只是我随便想的,并没有经过测试,但我认为这是在不把所有视图测试放入单元测试框架中的情况下,能够实现你想要的唯一方法。

撰写回答