测试django_如何像专业人士一样测试Django Signals
测试django
by Haki Benita
通过Haki Benita
如何像专业人士一样测试Django Signals (How to test Django Signals like a pro)
For a better reading experience, check out this article on my website.
为了获得更好的阅读体验,请在我的网站上查看此文章 。
Django Signals are extremely useful for decoupling modules. They allow a low-level Django app to send events for other apps to handle without creating a direct dependency.
Django Signals对于解耦模块非常有用。 它们允许低级Django应用程序发送事件供其他应用程序处理,而无需创建直接依赖项。
Signals are easy to set up, but harder to test. So In this article, I’m going to walk you through implementing a context manager for testing Django signals, step by step.
信号易于设置,但较难测试。 因此,在本文中,我将逐步指导您实现用于测试Django信号的上下文管理器。
用例 (The Use Case)
Let’s say you have a payment module with a charge function. (I write a lot about payments, so I know this use case well.) Once a charge is made, you want to increment a total charges counter.
假设您有一个带有收费功能的付款模块。 (我写了很多有关付款的书 ,所以我很好地了解了这个用例。)收费后,您想增加总收费计数器。
What would that look like using signals?
使用信号会是什么样?
First, define the signal:
首先,定义信号:
# signals.py
from django.dispatch import Signal
charge_completed = Signal(providing_args=['total'])
Then send the signal when a charge completes successfully:
然后在充电成功完成后发送信号:
# payment.py
from .signals import charge_completed
@classmethoddef process_charge(cls, total):
# Process charge…
if success: charge_completed.send_robust( sender=cls, total=total, )
A different app, such as a summary app, can connect a handler that increments a total charges counter:
另一个应用程序(例如摘要应用程序)可以连接一个使总费用计数器递增的处理程序:
# summary.py
from django.dispatch import receiver
from .signals import charge_completed
@receiver(charge_completed)def increment_total_charges(sender, total, **kwargs): total_charges += total
The payment module does not have to know the summary module or any other module handling completed charges. You can add many receivers without modifying the payment module.
支付模块不必知道汇总模块或处理已完成费用的任何其他模块。 您可以添加许多收款人,而无需修改付款模块 。
For example, the following are good candidates for receivers:
例如,以下是适合接收者的候选者:
- Update the transaction status.更新交易状态。
- Send an email notification to the user.向用户发送电子邮件通知。
- Update the last used date of the credit card.更新信用卡的最后使用日期。
测试信号 (Testing Signals)
Now that you got the basics covered, let’s write a test for process_charge
. You want to make sure the signal is sent with the right arguments when a charge completes successfully.
现在,您已经了解了基础知识,让我们为process_charge
编写一个测试。 您要确保在成功完成充电后以正确的参数发送信号。
The best way to test if a signal was sent is to connect to it:
测试是否已发送信号的最佳方法是连接到该信号:
# test.py
from django.test import TestCase
from .payment import chargefrom .signals import charge_completed
class TestCharge(TestCase):
def test_should_send_signal_when_charge_succeeds(self): self.signal_was_called = False self.total = None
def handler(sender, total, **kwargs): self.signal_was_called = True self.total = total
charge_completed.connect(handler)
charge(100)
self.assertTrue(self.signal_was_called) self.assertEqual(self.total, 100)
charge_completed.disconnect(handler)
We create a handler, connect to the signal, execute the function and check the args.
我们创建一个处理程序,连接到信号,执行功能并检查参数。
We use self
inside the handler to create a closure. If we hadn’t used self
the handler function would update the variables in its local scope and we won’t have access to them. We will revisit this later.
我们在处理程序中使用self
来创建一个闭包。 如果我们不使用self
则处理函数将在其本地范围内更新变量,而我们将无法访问它们。 我们稍后会再次讨论。
Let’s add a test to make sure the signal is not called if the charge failed:
让我们添加一个测试, 以确保如果充电失败,则不会调用该信号 :
def test_should_not_send_signal_when_charge_failed(self): self.signal_was_called = False
def handler(sender, total, **kwargs): self.signal_was_called = True
charge_completed.connect(handler)
charge(-1)
self.assertFalse(self.signal_was_called)
charge_completed.disconnect(handler)
This is working but it’s a lot of boilerplate! There must be a better way.
这是可行的,但是很多样板! 肯定有更好的办法。
输入上下文管理器 (Enter Context Manager)
Let’s break down what we did so far:
让我们分解一下到目前为止我们所做的:
- Connect a signal to some handler.将信号连接到某个处理程序。
- Run the test code and save the arguments passed to the handler.运行测试代码并保存传递给处理程序的参数。
- Disconnect the handler from the signal.断开处理程序与信号的连接。
This pattern sounds familiar…
这种模式听起来很熟悉...
Let’s look at what a (file) open context manager does:
让我们看一下(文件) 开放上下文管理器的作用:
- Open a file.打开一个文件。
- Process the file.处理文件。
- Close the file.关闭文件。
And a database transaction context manager:
和数据库事务上下文管理器 :
- Open transaction.公开交易。
- Execute some operations.执行一些操作。
- Close transaction (commit / rollback).关闭交易(提交/回滚)。
It looks like a context manager can work for signals as well.
似乎上下文管理器也可以处理信号 。
Before you start, think how you want to use a context manager to test signals:
在开始之前,请考虑如何使用上下文管理器测试信号:
with CatchSignal(charge_completed) as signal_args: charge(100)
self.assertEqual(signal_args.total, 100)
Nice, let’s give it a try:
好的,让我们尝试一下:
class CatchSignal: def __init__(self, signal): self.signal = signal self.signal_kwargs = {}
def handler(sender, **kwargs): self.signal_kwrags.update(kwargs)
self.handler = handler
def __enter__(self): self.signal.connect(self.handler) return self.signal_kwrags
def __exit__(self, exc_type, exc_value, tb): self.signal.disconnect(self.handler)
What we have here:
我们在这里拥有什么:
- You initialized the context with the signal you want to “catch”.您使用要“捕获”的信号初始化了上下文。
- The context creates a handler function to save the arguments sent by the signal.上下文创建一个处理函数,以保存信号发送的参数。
You create closure by updating an existing object (
signal_kwargs
) onself
.您可以通过更新现有的对象(创建封闭
signal_kwargs
上)self
。- You connect the handler to the signal.您将处理程序连接到信号。
Some processing is done (by the test) between
__enter__
and__exit__
.(通过测试)在
__enter__
和__exit__
之间进行了一些处理。- You disconnect the handler from the signal.您从信号断开处理程序。
Let’s use the context manager to test the charge function:
让我们使用上下文管理器测试计费功能:
def test_should_send_signal_when_charge_succeeds(self): with CatchSignal(charge_completed) as signal_args: charge(100) self.assertEqual(signal_args[‘total’], 100)
This is better, but how would the negative test look like?
这样比较好,但是负面测试会是什么样子?
def test_should_not_send_signal_when_charge_failed(self): with CatchSignal(signal) as signal_args: charge(100) self.assertEqual(signal_args, {})
Yak, that’s bad.
k牛,不好。
Let’s take another look at the handler:
让我们再看一下处理程序:
- We want to make sure the handler function was invoked.我们要确保处理程序函数被调用。
- We want to test the args sent to the handler function.我们要测试发送到处理程序函数的参数。
Wait… I already know this function!
等等... 我已经知道这个功能了!
输入模拟 (Enter Mock)
Let’s replace our handler with a Mock:
让我们用Mock替换处理程序:
from unittest import mock
class CatchSignal: def __init__(self, signal): self.signal = signal self.handler = mock.Mock()
def __enter__(self): self.signal.connect(self.handler) return self.handler
def __exit__(self, exc_type, exc_value, tb): self.signal.disconnect(self.handler)
And the tests:
和测试:
def test_should_send_signal_when_charge_succeeds(self): with CatchSignal(charge_completed) as handler: charge(100) handler.assert_called_once_with( total=100, sender=mock.ANY, signal=charge_completed, )
def test_should_not_send_signal_when_charge_failed(self): with CatchSignal(charge_completed) as handler: charge(-1) handler.assert_not_called()
Much better!
好多了!
You used the mock for exactly what it should be used for, and you don’t need to worry about scope and closure.
您使用了该模拟程序来确定它的确切用途,而不必担心范围和闭包。
Now that you have this working, can you make it even better?
现在您已经可以使用它了 , 您可以使其变得更好吗?
输入contextlib (Enter contextlib)
Python has a utility module for handling context managers called contextlib.
Python有一个用于处理上下文管理器的实用程序模块,称为contextlib 。
Let’s rewrite our context using contextlib
:
让我们使用contextlib
重写contextlib
:
from unittest import mockfrom contextlib import contextmanager
@contextmanagerdef catch_signal(signal): """Catch django signal and return the mocked call.""" handler = mock.Mock() signal.connect(handler) yield handler signal.disconnect(handler)
I like this approach better because it’s easier to follow:
我更喜欢这种方法,因为它更容易遵循:
- The yield makes it clear where the test code is executed.收益率清楚地表明了在哪里执行测试代码。
No need to save objects on
self
because the setup code (enter and exit) are in the same scope.因为设置代码(输入和退出)在同一范围内,所以无需
self
保存对象。
And that’s it — 4 lines of code to rule them all! Profit!
就是这样-4行代码来统治它们! 利润!
翻译自: https://www.freecodecamp.org/news/how-to-testing-django-signals-like-a-pro-c7ed74279311/
测试django
相关文章:

C#中静态方法的运用和字符串的常用方法(seventh day)
又来到了今天的总结时间,由于昨天在云和学院学的知识没有弄懂,今天老师又专门给我们非常详细地讲了一遍,在这里非常谢谢老师。O(∩_∩)O 话不多说,下面就开始为大家总结一下静态方法的运用和字符串的常用方法。 理论:静…

raid 磁盘阵列
mkdir /uuu #建挂载目录echo "- - -" > /sys/class/scsi_host/host2/scan #扫描新硬盘 lsblk #查看 parted /dev/sdb #分区 parted /dev/sdc lsblk mdadm -Cv /dev/md1 -l1 -n2 -c128 /dev/sd[b,c]1 #raid1配置, /dev/md1名字&#…

iOS 13 如何删除SceneDelegate
Xcode11之后新创建的工程会多出两个文件SceneDelegate。那么我们如何让它变回之前的那样的工程呢。 一、将这两个文件删除。 会报错There is no scene delegate set. A scene delegate class must be specified to use a main storyboard file. 二、将Info.plist 中的 SceneMai…

女性程序员大会ghc_在女性科技大会上成为男人的感觉
女性程序员大会ghcby Elijah Valenciano通过伊莱贾瓦伦西亚诺 在女性科技大会上成为男人的感觉 (What It’s Like to be a Man at a Women’s Tech Conference) To be honest, I was very nervous. A few panicked thoughts started to flood my mind as I prepared myself to…

cf776G.Sherlock and the Encrypted Data
题意:对于一个16进制数x,把x的各个数位拿出来,设其为t1,t2,...,定义s(x)为2^t1|2^t2|...,如x0x3e53,则s(x)2^3|2^14|2^5|2^316424.给出q组询问l,r(l,r也是16进制数,不超过15位),求[l,r]中有多少个数x满足x^s(x)<x. 这题题解写的是个状压数位dp,但是蒟蒻不会数位dp,自己YY了一…

c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]
说明:文章中关于operator实现的示例,从语法上是对的,但逻辑和习惯上都是错误的。 参见另一篇专门探究operator的文章:《c,operator》http://www.cnblogs.com/mylinux/p/4113266.html 1.构造函数与析构函数不会被继承&a…

json转换模型利器--JSONExport
JSONExport 从json 到 Model ,如此的方便 swift oc java 全部支持

亚马逊ses如何发qq_使用Amazon SES发送电子邮件
亚马逊ses如何发qqby Kangze Huang黄康泽 使用Amazon SES发送电子邮件 (Sending emails with Amazon SES) 完整的AWS Web样板-教程3 (The Complete AWS Web Boilerplate — Tutorial 3) 目录 (Table of Contents) Part 0: Introduction to the Complete AWS Web Boilerplate第…

源码-0205-02--聊天布局
还真是失败,搞了两天遇到了布局cell高度总是出差的问题,cell height不是高很多很多,就是就是矮到没有的情况。。。。糟糕透顶待解救~ 聊天布局 // // XMGChatingViewController.m // 07-聊天布局 #import "XMGChatingViewC…

js实现页面跳转的几种方式
第一种:<script language"javascript" type"text/javascript"> window.location.href"login.jsp?backurl"window.location.href; </script>第二种: <script language"javascript&q…

Mac 升级系统 pod 命令无效
mac 升级完最新的系统之后 使用pod 命令之后无效报错 -bash: /usr/local/bin/pod: /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby: bad interpreter: No such file or directory 解决方案 sudo gem install -n /usr/local/bin cocoapods

node seneca_使用Node.js和Seneca编写国际象棋微服务,第1部分
node seneca(This is Part 1 of a three-part series [Part 2, Part 3])(这是一个由三部分组成的系列文章的第1部分[ 第2 部分 , 第3部分 ]) I’ve begun wrapping my head around microservices. Up to this time I regarded it as a scalability pattern and ove…

Ubuntu中基于QT的系统网线连接状态的实时监视
1.必要准备 需包: #include <QNetworkInterface> 2.实现获取当前的网线连接状态 以下是自己在网络上搜到的一个解决方法,且没有加入iface.flags().testFlag(QNetworkInterface::IsRunning) 这一逻辑判断,经测试实时性极不可靠ÿ…

iOS 开发者账号 到期续费问题
https://blog.csdn.net/liruiqing520/article/details/104043221

[转载]Using ngOptions In AngularJS
http://odetocode.com/blogs/scott/archive/2013/06/19/using-ngoptions-in-angularjs.aspx?utm_sourcetuicool转载于:https://www.cnblogs.com/Benoly/p/4097213.html

graphql_GraphQL的稳步上升
graphqlToday GitHub announced that the next version of their API will use a new technology developed by Facebook called GraphQL.今天,GitHub宣布其API的下一版本将使用Facebook开发的一项名为GraphQL的新技术。 GraphQL may eventually come to replace t…

转: windows系统下mysql出现Error 1045(28000) Access Denied for user 'root'@'localhost'
windows系统下mysql出现Error 1045(28000) Access Denied for user rootlocalhost 转自 http://zxy5241.spaces.live.com/blog/cns!7682A3008CFA2BB0!361.entry 在windows操作系统安装MySQL数据库,碰到Error 1045(28000) Access Denied for user rootlocalhost 错误…

正则表达式的字符、说明和其简单应用示例
字符和其含义 字符 含义 \ 转义字符,将一个具有特殊功能的字符转义为一个普通的字符 ^ 匹配字符串的开始位置 $ 匹配字符串的结束位置 * 匹配前面的0次或多次的子表达式 …

iOS 设置UILabel 的行间距
// // UILabelLineSpace.h//#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGINinterface UILabel (LineSpace)/**设置文本,并指定行间距param text 文本内容param lineSpacing 行间距*/ -(void)setText:(NSString*)text lineSpacing:(CGFloat)lineSpacing;endNS_ASSUME_N…

倦怠和枯燥_启动倦怠
倦怠和枯燥by Elie Steinbock埃莉斯坦博克(Elie Steinbock) 启动倦怠 (Start-up Burnout) Shabbat is the seventh day of the week. It starts on Friday night and ends on the following evening, Saturday. (A day starts in the evening for the Jews.) It’s also the J…

客户端处理包方法
不同包客户端的处理方法 对于那种事件类型的 连接上了,连接失败了,断开连接了 bool NGP::OnConnected() {std::lock_guard<std::mutex> lock(m_PktMutex);//加锁是因为runonce应该是另一个线程m_queFunctions.push(std::bind(&NGP::Connect2Se…

0011_练习题d1
__author__ qq593 #!/usr/bin/env python #-*- coding:utf-8 -*- #使用while循环输入1 2 3 4 5 6 8 9 10 a1 while True:print(a)if(a10):breakif (a7):a1continuea1 __author__ qq593 #!/usr/bin/env python #-*- coding:utf-8 -*- #求1-100所有数的和 a1 sum00 while(a<…

iOS 仿微信灵活添加标签
iOS 仿微信灵活添加标签 原作者的github 地址 喜欢的点赞 https://github.com/DreamFlyingCow/TTTags 效果如下,iOS 13 访问私有属性 会崩溃,自己修改一下即可 TTTagView.m 文件修改如下 我的备份:https://github.com/AlexanderYeah/TTTa…

css 倒三角_倒三角结构:如何管理大型CSS项目
css 倒三角by Luuk GruijsLuuk Gruijs着 倒三角结构:如何管理大型CSS项目 (The Inverted Triangle Architecture: how to manage large CSS Projects) You’re assigned a small task to fix some little styling issues here and there. You’ve found the correc…

列举一些常见的系统系能瓶颈 Common Bottlenecks
http://www.nowamagic.net/librarys/veda/detail/2408在 Zen And The Art Of Scaling - A Koan And Epigram Approach 一文中, Russell Sullivan 提出一个很有趣的设想:一共有20种经典的瓶颈。这听起来就像只有20种基本的故事情节(20 basic s…

Zeal 离线API文档浏览器
zeal是一个windows上的开源的离线文档浏览工具,基于docset格式,可以兼容全部dash的文档。zeal没有代码片段管理的功能,只提供文档浏览功能,不过windows下的用户可算是有的用了。dash目前只提供mac上的版本,作者说有往w…

iOS scrollToItemAtIndexPath 无效的解决方案
在UITableview中放置的UICollectionView,然后设置滚动没有效果scrollToItemAtIndexPath - (void)layoutSubviews {[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.selectedIdx inSection:0] atScrollPosition:UICollectionViewScrollPositio…

机器学习编程语言_我应该选择哪种编程语言? 我应该专注于前端吗? 后端? 机器学习?...
机器学习编程语言by Preethi Kasireddy通过Preethi Kasireddy 我应该选择哪种编程语言? 我应该专注于前端吗? 后端? 机器学习? (What programming language should I pick? Should I focus on front-end? Back-end? Machine l…

spdlog源码阅读 (1): sinks
0. spdlog简单介绍 spdlog 是一个快速的 C 日志库,只包含头文件,兼容 C11。项目地址 特性: 非常快只包含头文件无需依赖第三方库支持跨平台 - Linux / Windows on 32/64 bits支持多线程可对日志文件进行循环输出可每日生成日志文件支持控制台日志输出可选…

什么样的程序员才算成熟? 让程序员认清自己的所处的阶段
http://www.nowamagic.net/librarys/veda/detail/1450程序员在经历了若干年编程工作之后,很想知道自己水平到底如何?自己是否已经成为成熟的程序员?虽然程序员会对自己有一个自我评价,但是,自己的评价和社会的评价、专…