【问题标题】:How to properly use assertRaises() with str type objects如何正确使用带有 str 类型对象的 assertRaises()
【发布时间】:2017-01-25 21:14:44
【问题描述】:

我正在尝试编写一个简单的单元测试来测试我的fake_user's(通过FactoryBoy 创建)username 是否已经存在。 (即有人已经创建了一个具有相同用户名的用户)。

一个简单的测试版本如下:

# factories.py
import factory


class BaseUserFactory(factory.DjangoModelFactory):
    """A factory for creating User objects."""

    username = factory.Faker('word')
    email = factory.lazy_attribute(lambda x: '%s@email.com' % (x.username))

    class Meta:
        """User factory Meta attributes."""

        model = models.User
        django_get_or_create = ('username', 'email')

一个简单的测试:

#test_user.py
from django.test import TestCase
from django.apps import apps

from . import factories


class TestAccountModels(TestCase):
    """Testing the User models."""

    users = [user.username for user in models.User.objects.all()]
    fake_user = factories.BaseUserFactory.create()

    def test_username_available(self):
        self.assertIn(self.fake_user.username, self.users)

现在就上下文而言,假设我的(已经存在的)username(s) 是“Brody”和“Sam”。当test_username_available 运行时,我会回来:

self.assertIn(self.fake_user.username, self.users)
AssertionError: 'suscipit' not found in ['Sam', 'Brody']

我的问题是这样的:

我想写一个测试来测试这个特定的错误。我尝试过这样的事情:

    def test_clean_username(self):
    self.assertRaises(
        AssertionError, '"{0}" unexpectedly found in "{1}"'.format(
            self.proposed_username,
            self.taken_usernames,
            )
        )

但不断收到str object is not callable(在taken_usernames);我明白了,但似乎找不到解决方法

地点:

proposed_username = self.fake_user.username
self.taken_usernames = [self.user.username for self.user in self.user]

我尝试过以不同的方式重新工作,但似乎问题与此有关(从另一个答案,我在某个地方记下了这个,所以很遗憾没有链接):

You are using self.assertRaises() incorrectly. 
It has to call the test function for you, in order to catch the exception
self.assertRaises(mouse16.BadInternalCallException,
stack.insertn, [8, 4, 12], 16)

You were passing in the result of the stack.insertn() call (which didn't raise an exception, but returned either None or an integer instead), and that return value is not callable.
An older version of the method accepted None for that argument as well, which is no longer supported, hence the deprecation warning when None is passed in versus an integer.
The better way is to use self.assertRaises() as a context manager:
with self.assertRaises(mouse16.BadInternalCallException):
stack.insertn([8, 4, 12], 16

我需要使用上下文管理器吗?我觉得这应该更容易,我让它变得比它必须的更难。最初我试图从模型中提取属性。dict 但最终接受了别人的建议并使用了一些列表理解。

这在 TestCase 方法/类之外有效。我觉得应该有一个简单的解决方法,但我对 python/django 的了解还不够。

感谢您的帮助!

编辑:下面提供的答案似乎很有希望,但无论如何似乎都通过了。我希望测试失败,除非正则表达式(即错误消息)和错误类型(在本例中为 AssertionError)完全匹配

【问题讨论】:

  • 但是您似乎没有将任何类型的函数传递给 assertRaises,只是两组用户名。你期望发生什么?
  • 不管怎样,你为什么要测试另一个测试引发的错误?
  • 好吧,我本身不想进行第二次测试。我在想测试这个特定的错误会比仅仅测试 abc 中是否存在 xyz 更好。只是认为这是一种更好的做法,可以说是测试错误..
  • 基本上我想测试当一个人尝试添加一个存在或不存在于数据库中的用户名时会弹出的断言错误。我希望 .format 可以让我在 assertRaises 中插入“假”和“真实”名称,但显然没有正确使用它

标签: python django unit-testing django-models


【解决方案1】:

assertRaises 的用法如下所示:

self.assertRaises(InvalidOperation, Decimal, '25,34')

除非 callableObj 引发类 excClass 的异常,否则失败 当使用参数 args 和关键字参数 kwargs 调用时。

Decimal 是示例中的可调用对象,'25,34' 是 arg。

在你的情况下使用 assertRaisesMessage:

def test_clean_username(self):
  self.assertRaisesMessage(
      AssertionError, '"{0}" unexpectedly found in "{1}"'.format(
          self.proposed_username,
          self.taken_usernames,
          )
      )

断言在引发的消息中找到了 expected_message 例外。

【讨论】:

  • 这似乎很有希望......谢谢你的解释,不管我在字符串中有什么,它似乎都通过了?例如,我将 'unexpectedly found in' 切换为 'yoyoyo' 它仍然通过...是因为:断言在引发异常的消息中找到了 expected_message。所以它的传递是因为它存在于消息中?我正在寻找或多或少的完全匹配
  • 如果我们查看内部实现,它使用 assertIn。如果您预期消息“test”和异常“xxx test yyy”,那么它通过了。所以尽量使用确切的消息进行测试。
  • 你测试了吗?
  • 说得通;谢谢你。我现在正在调查它。我几乎在想我应该使用常规的 'assert ==' 可能会更容易。似乎没有 assertRaisesExactMessage ......这是 assertRaises/Warns 正则表达式派上用场的吗?
  • assertRaisesRegexp 正在使用 re.search() 因此在相同数据上的行为没有差异。 assertRaisesMessage 检查异常类型和确切消息。为测试提供预期数据是您的任务,无需精确匹配,因为在测试中您会重现特定情况。
猜你喜欢
  • 2011-08-31
  • 2018-10-01
  • 2014-12-22
  • 2023-03-25
  • 1970-01-01
  • 2019-03-12
  • 2011-07-09
  • 2022-01-19
  • 1970-01-01
相关资源
最近更新 更多