django单元测试异常

时间:2016-08-17 05:50:35

标签: python django unit-testing

我在django项目中工作并编写服务以便于apis.in服务,我有这样的类和类方法,

class ProductService(object):
    def delete_product(self, product_id, deleting_user):
        try:
            product = Product.objects.get(pk=product_id)
        except Product.DoesNotExist:
            raise ObjectDoesNotExist(_('no product found for this id {}'.format(product_id)))

        try:
            deleting_user = Customer.objects.get(owner=deleting_user)
        except Customer.DoesNotExist:
            raise ValidationError(_('No owner found for this deleting user'))

我已经为此方法编写了以下单元测试,

def setUp(self):
    self.product_service = ProductService()
    self.wrong_id = 0
    self.right_id = 1
    self.right_user = _user
    self.wrong_user = wrong_user  

def test_raise_does_not_exist_error_for_wrong_product_id(self):
    with self.assertRaises(ObjectDoesNotExist) as e:
        self.product_service.delete_product(
            self.product_id=self.wrong_id,
            user=self.right_user     
        )
    self.assertEqual(e.exception.message, 'no product found for this id {}'.format(wrong_id))

def test_raise_validtaion_error_for_wrong_deleting_user(self):
    with self.assertRaises(ObjectDoesNotExist) as e:
        self.product_service.delete_product(
            self.product_id=self.right_id,
            user=self.wrong_user     
        )
    self.assertEqual(e.exception.message, 'No owner found for this deleting user') 
到目前为止一切顺利,所有测试都没问题!

但是,我说我有很多像这样的测试用例。也就是说,测试错误'如果将来我必须更改错误消息,那么我也必须更改测试用例,这可能是一个MESS,但另一方面我还需要适当地测试errors

问题是,我如何测试不同场景的异常?因为我测试的方式,虽然现在可以,但是对于未来,它可能是一团糟,所以我需要你们的一些建议来以有效的方式处理这种情况。

3 个答案:

答案 0 :(得分:3)

我们在这种情况下使用Enum:

class ProductErrors(Enum):
    not_found = "Product {} not found"
    doesnt_exist = "Product {} doesn't exist"

这将允许您在测试中使用此枚举并检查它:

def test_raise_does_not_exist_error_for_wrong_product_id(self):
    with self.assertRaises(ObjectDoesNotExist) as e:
        self.product_service.delete_product(
            self.product_id=self.wrong_id,
            user=self.right_user     
        )
    self.assertEqual(e.exception.message, ProductErrors.not_found.value.format(wrong_id))

答案 1 :(得分:0)

如果我理解正确,你会担心重复的代码很多,如果有任何改变,就会很难维护测试(例如:方法的签名接受新参数等)。

介绍一个帮助器方法可能有所帮助(注意不要创建太多使用彼此的辅助方法,因为测试代码将变得难以遵循),例如:

def test_raise_validtaion_error_for_wrong_deleting_user(self):
    self.assert_delete_product_raises_error(
        exception_cls=ObjectDoesNotExist,
        error_message='No owner found for this deleting user',
        product_id=self.right_id,
        user=self.wrong_user
    )

def assert_delete_product_raises_error(self, exception_cls, error_message, **delete_product_kwargs):
    with self.assertRaises(exception_cls) as raised:
         self.product_service.delete_product(**delete_product_kwargs)
    self.assertEqual(error_message, raised.exception.message)

这种方式应该为底层方法改变(例如:一个新的,必需的参数,与这些测试无关,它可以作为默认值添加到辅助方法中,并且实际测试将保持稳定。 / p>

但如上所述,避免使用太多辅助方法和默认值 - 如果有很多辅助方法和默认值,请考虑将测试类重构为多个测试类。

答案 2 :(得分:0)

既然我理解了最初的问题,即目标是允许在不让测试中断的情况下更改错误文本,我建议使用translations。而不是错误的完整英文文本,只需使用键/代号。 I.e。:而不是JavascriptExecutor je = (JavascriptExecutor) dr; je.executeScript("window.scrollTo(0,document.body.scrollHeight);") dr.findElement(By.partialLinkText("Mail"))); 使用_('No owner found for this deleting user'))并在PO文件中翻译实际文本。

当然,这可能导致其他问题(需要确保所有翻译字符串实际翻译,翻译期间不会遗漏动态内容(变量),并且所有翻译文件都已正确部署),但它会以问题的方式使测试稳定