为什么只有在运行完整的测试套件时,django测试才会失败?

时间:2015-04-22 08:56:12

标签: python django unit-testing

我在Django 1.5中测试过这些条件:

  • 独立运行时
  • 运行完整TestCase
  • 当我的所有应用测试都已运行时

但是当使用python manage.py test运行完整的测试套件时,它会失败。为什么会发生这种情况?

异常测试使用django.test.Client POST将某些数据发送到端点,然后测试检查对象是否已成功更新。其他一些应用可以修改测试客户端或数据本身吗?

我尝试了一些打印调试,我看到所有数据都按预期发送和接收。特定的失败是当我尝试从db中获取要更新的对象时引发的不存在的异常。奇怪的是,在异常处理程序本身中,我可以查询该类型的所有对象,并看到目标对象确实存在。

编辑:

当我发现我正在idUser而不是idUserProfile查询目标对象时,我的问题已得到解决,但它已经解决了仍然让我感到困惑的是,这会在某些情况下起作用,但在其他情况下会失败。

我还发现测试会因python manage.py test auth <myapp>

而失败

6 个答案:

答案 0 :(得分:17)

听起来你的问题不涉及嘲讽,但我只花了一整天调试一个有类似症状的问题,而你的问题是我在寻找解决方案时出现的第一个问题,所以我想分享一下这里的解决方案,以防它对其他人有用。就我而言,问题如下。

我有一个单独的测试可以隔离通过,但是作为我的完整测试套件的一部分运行时会失败。在我的一个视图函数中,我使用的是Django send_mail()函数。在我的测试中,我没有在每次运行测试时向我发送电子邮件,而是在我的测试方法中patch编辑send_mail

from mock import patch
...

def test_stuff(self):
    ...

    with patch('django.core.mail.send_mail') as mocked_send_mail:

    ...

这样,在调用我的视图函数后,我可以测试send_mail被调用:

self.assertTrue(mocked_send_mail.called)

这在单独运行测试时工作正常,但在套件中运行其他测试时失败。失败的原因是,当它作为套件的一部分运行时,其他视图会事先被调用,从而导致views.py文件被加载,导致send_mail在之前导入有机会patch它。因此,在我的视图中调用send_mail时,实际的send_mail会被调用,而不是我的修补版本。当我单独运行测试时,该函数在导入之前被模拟,因此修补后的版本最终会在加载views.py时导入。这种情况在mock documentation中有所描述,我之前已经阅读了几次,但是在学习了很多方法之后现在明白了......

解决方案很简单:我只是修补了django.core.mail.send_mail - views.py中已导入的版本,而不是修补myapp.views.send_mail。换句话说:

with patch('myapp.views.send_mail') as mocked_send_mail:
...

答案 1 :(得分:1)

另一种可能性是你在测试类的setUp中断开了信号,并且没有在tearDown中重新连接。这解释了我的问题。

答案 2 :(得分:1)

有很多非确定性可以来自涉及数据库的测试。

例如,除非您按顺序执行,否则大多数数据库都不提供确定性选择。这导致了奇怪的行为,当星星对齐时,数据库以不同于您预期的顺序返回事物,并且测试看起来像

result = pull_stuff_from_database()
assert result[0] == 1
assert result[1] == 2

将失败,因为result[0] == 2 and result[1] == 1

奇怪的不确定行为的另一个来源是id自动增量以及某种类型的排序。

假设每个测试都会创建两个项目,并在执行断言之前按项目名称进行排序。单独运行时,“项目1”和“项目2”正常工作并通过测试。但是,当您运行整个套件时,其中一个测试会生成“第9项”和“第10项”。 “项目10”在“项目9”之前排序,因此您的测试失败,因为订单被翻转。

答案 3 :(得分:0)

试试这个来帮助你调试:

./manage.py test --reverse

在我的情况下,我意识到一个测试正在更新某些数据,这会导致以下测试失败。

答案 4 :(得分:0)

因此,我首先阅读@elethan的答案,然后说“嗯,这当然不是我的问题,我没有修补任何东西”。但是事实证明,我确实是在另一个测试套件中修补了一个方法,在其余的测试运行期间,该方法始终处于永久性修补状态。

我正在进行这种事情;

send_comment_published_signal_mock = MagicMock()
comment_published_signal.send = send_comment_published_signal_mock

您可以看到如果在运行测试套件后未清除某些内容,这将是一个问题的原因。在我的情况下,解决方案是使用有用的with来限制范围。

signal = 'authors.apps.comments.signals.comment_published_signal.send'
with patch(signal) as comment_published_signal_mock:
    do_your_test_stuff()

这是最简单的部分,在您知道要查找的位置之后。有罪测试可能来自任何地方。解决方案是将失败的测试和其他测试一起运行,直到找到原因为止,然后再次逐个模块逐个缩小范围。

类似的东西

./manage.py test A C.TestModel.test_problem
./manage.py test B C.TestModel.test_problem
./manage.py test D C.TestModel.test_problem

然后递归返回,例如,如果B是有问题的孩子;

./manage.py test B.TestModel1 C.TestModel.test_problem
./manage.py test B.TestModel2 C.TestModel.test_problem
./manage.py test B.TestModel3 C.TestModel.test_problem

answer为所有这些提供了很好的解释。

这个答案是在django的上下文中,但实际上可以应用于任何python测试。

祝你好运。

答案 5 :(得分:0)

这也在我身上发生。

分别运行测试时,它们通过了,但是使用./manage.py test运行所有测试均失败了。

我的问题是因为我有一些测试是从unittest.TestCase继承而来的,而不是从django.test.TestCase继承的,所以某些测试失败了,因为以前的测试在数据库中有寄存器。

使所有测试都继承自django.test.TestCase之后,这个问题就消失了。

我在https://stackoverflow.com/a/436795/6490637上找到了答案