- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我承认,我没有进行太多的单元测试……但我愿意。话虽如此,我有一个非常复杂的注册过程,我想对其进行优化以简化单元测试。 我正在寻找一种方法来构建我的类,以便我将来可以更轻松地测试它们。 所有这些逻辑都包含在 MVC 框架中,因此您可以假设 Controller 是所有实例化的根。
为简化起见,我主要问的是如何设置一个系统,您可以在其中管理任意数量的带有 CRUD 更新的第三方模块。这些第三方模块都是 RESTful API 驱动的,响应数据存储在本地副本中。像删除用户帐户这样的事情需要触发所有相关模块(我将其称为提供者)的删除。这些提供者可能依赖于另一个提供者,因此删除/创建的顺序很重要。我对我应该专门使用哪些设计模式来支持我的应用程序很感兴趣。
注册跨越多个类并将数据存储在多个数据库表中。这是不同提供者和方法的顺序(它们不是静态的,只是为了简洁起见):
Provider::create('external::create-user')
在特定提供商的特定步骤启动注册。第一个参数中的双冒号语法表示该类应该在 providerClass::providerMethod
上触发创建。 .我做了一个一般性的假设 Provider
将是方法的接口(interface) create()
, update()
, delete()
所有其他提供商都会实现它。 如何实例化这可能是您需要帮助我的事情。 $user = Provider_External::createUser()
在外部 API 上创建用户,返回成功,然后用户存储在我的数据库中。 $customer = Provider_Gapps_Customer::create($user)
在第三方 API 上创建客户,返回成功,并在本地存储。 $subscription = Provider_Gapps_Subscription::create($customer)
在第三方 API 上创建与先前创建的客户相关联的订阅,返回成功并在本地存储。 Provider_Gapps_Verification::get($customer, $subscription)
从外部 API 检索一行。此信息存储在本地。另一个电话是我跳过以保持简洁。 Provider_Gapps_Verification::verify($customer, $subscription)
执行外部 API 验证过程。其结果存储在本地。 Provider::create('external')
我只是指定开始注册的起始步骤。
provider_steps
,我在这里列出了每个步骤以及它后面的
success_step
和
failure_step
(用于回滚和删除)。该表如下所示:
# the id of the parent provider row
provider_id int(11) unsigned primary key,
# the short, slug name of the step for using in codebase
step_name varchar(60),
# the name of the method correlating to the step
method_name varchar(120),
# the steps that get triggered on success of this step
# can be comma delimited; multiple steps could be triggered in parallel
triggers_success varchar(255),
# the steps that get triggered on failure of this step
# can be comma delimited; multiple steps could be triggered in parallel
triggers_failure varchar(255),
created_at datetime,
updated_at datetime,
index ('provider_id', 'step_name')
最佳答案
您已经在使用 pub/sub 模式,这似乎很合适。除了您上面的评论外,我会考虑将有序列表作为优先级机制。
但是,每个订阅者都关心其依赖项的操作顺序以触发成功/失败,这仍然令人不快。依赖项通常看起来像是属于一棵树,而不是一个列表。如果您将它们存储在树中(使用复合模式),那么内置递归将能够通过首先清理其依赖项来清理每个依赖项。这样您就不必再担心清理发生的优先顺序 - 树会自动处理。
您可以使用树来存储发布/订阅订阅者,几乎就像使用列表一样容易。
使用测试驱动的开发方法可以满足您的需求,并确保您的整个应用程序不仅完全可测试,而且完全被测试覆盖,证明它可以满足您的需求。我首先会准确描述您需要做什么才能满足一个要求。
您知道自己想要做的一件事是添加一个提供程序,因此 TestAddProvider() 测试似乎是合适的。请注意,此时它应该非常简单,并且与复合模式无关。一旦它起作用,您就会知道提供者有一个依赖项。创建一个 TestAddProviderWithDependent() 测试,看看效果如何。同样,它不应该很复杂。接下来,您可能想要 TestAddProviderWithTwoDependents(),这就是列表将被实现的地方。一旦它起作用,您就知道您希望 Provider 也是一个 Dependent,因此新的测试将证明继承模型有效。从那里,您将添加足够的测试以说服自己添加提供者和依赖项的各种组合有效,并测试异常条件等。仅从测试和需求中,您将快速得出满足您需求的复合模式.在这一点上,我实际上会打开我的 GoF 副本,以确保我了解选择复合图案的后果,并确保我没有添加不合适的疣。
另一个已知的要求是删除提供者,因此创建一个 TestDeleteProvider() 测试,并实现 DeleteProvider() 方法。您也不会远离提供程序删除其依赖项,因此下一步可能是创建 TestDeleteProviderWithADependent() 测试。复合模式的递归在这一点上应该是显而易见的,您应该只需要再进行几次测试就可以说服自己深嵌套的提供者、空叶子、宽节点等,所有这些都会正确地清理自己。
我认为您的提供商需要实际提供他们的服务。是时候测试调用提供程序(使用模拟提供程序进行测试),并添加测试以确保它们可以找到它们的依赖项。同样,复合模式的递归应该有助于构建依赖项列表或正确调用正确提供程序所需的任何内容。
您可能会发现必须按特定顺序调用提供程序。此时,您可能需要为复合树中每个节点的列表添加优先级。或者,您可能必须构建一个完全不同的结构(例如链表)才能以正确的顺序调用它们。使用测试并慢慢接近它。您可能仍然有人担心您以特定的外部规定顺序删除家属。在这一点上,您可以使用您的测试向怀疑者证明您将始终安全地删除它们,即使不是按照他们所想的顺序。
如果你一直做对了,你之前的所有测试都应该继续通过。
然后是棘手的问题。如果您有两个共享公共(public)依赖项的提供程序怎么办?如果您删除一个提供程序,它是否应该删除其所有依赖项,即使不同的提供程序需要其中之一?添加测试,并实现您的规则。我想我会通过引用计数来处理它,但也许您想要第二个实例的提供者的副本,所以您永远不必担心共享子项,并且您可以通过这种方式使事情变得更简单。或者也许这在您的域中从来都不是问题。另一个棘手的问题是您的提供者是否可以具有循环依赖关系。您如何确保不会陷入自我参照循环?编写测试并弄清楚。
在您弄清楚整个结构之后,您才会开始考虑用于描述此层次结构的数据。
这就是我会考虑的方法。它可能不适合您,但由您决定。
关于php - 我如何构建我的类以便更轻松地进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16826582/
我有一个功能是转换 ADO Recordset 进入html: class function RecordsetToHtml(const rs: _Recordset): WideString; 该函
经过几天的研究和讨论,我想出了这种方法来收集访客的熵(你可以看到我的研究历史here) 当用户访问时,我运行此代码: $entropy=sha1(microtime().$pepper.$_SERVE
给定一个无序列表 List ,我需要查找是否存在 String与提供的字符串匹配。 所以,我循环 for (String k : keys) { if (Utils.keysM
我已经搜索过这个问题,但没有找到我正在寻找的答案。 基本上,我想将类构造函数包装在 try/except 子句中,以便它忽略构造函数内特定类型的错误(但无论如何都会记录并打印它们)。我发现做到这一点的
我有一组三个数字,我想将一组数字与另一组数字进行比较。即,第一组中的每个数字小于另一组中的至少一个数字。需要注意的是,第一组中的下一个数字必须小于第二组中的不同数字(即,{6,1,6} 对 {8,8,
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 9 年前。 Improve this
首先介绍一下背景: 我正在开发一个带有 EJB 模块和应用程序客户端模块的企业应用程序 (ear)。我还使用 hibernate JPA 来实现持久性,并使用 swingx 来实现 GUI。这些是唯一
我正在尝试在我的上网本上运行 Eclipse 以便能够为 Android 进行开发。 您可能已经猜到了,Eclipse 非常慢,并且不容易有效地开发。 我正在使用 Linux Ubuntu 并且我还有
for row, instrument in enumerate(instruments): for col, value in enumerate(instrument):
return not a and not b ^ 我如何以更好的格式表达它 最佳答案 DeMorgan's Law , 也许? return not (a or b) 我认为在这一点上已经足够简单了
我正在尝试让 Font Awesome 图标看起来更 slim https://jsfiddle.net/cliffeee/7L6ehw9r/1/ . 我尝试使用“-webkit-text-strok
假设我有一个名为 vals 的数据框,如下所示: id…………日期…………min_date…… .........最大日期 1…………2016/01/01…………2017/01/01…………2018/
是否有更 Pythonic 的方式来做到这一点?: if self.name2info[name]['prereqs'] is None: se
我有一个函数可以将一些文本打印到它接收到的 ostream&。如果 ostream 以终端为目标,我想让它适应终端宽度,否则默认为某个值。 我现在做的是: 从 ostream 中获取一个 ofstre
这个问题在这里已经有了答案: Should a retrieval method return 'null' or throw an exception when it can't produce
我有这个 bc = 'off' if c.page == 'blog': bc = 'on' print(bc) 有没有更 Pythonic(和/或更短)的方式在 Python 中编写? 最佳
输入:一个包含 50,000 行的 CSV;每行包含 910 列值 0/1。 输出:运行我的 CNN 的数据框。 我编写了一个逐行读取 CSV 的代码。对于每一行,我将数据分成两部分,称为神经元(90
据我所知,with block 会在您退出 block 后自动调用 close(),并且它通常用于确保不会忘记关闭一个文件。 好像没有技术上的区别 with open(file, 'r+') as f
我有一个使用 Entity Framework V6.1.1 的 MVC 5 网站。 Entity Framework DbContext 类和模型最初都在网站项目中。这个项目有 3 个 DbCont
我是编程新手,在尝试通过将 tableView 和关联 View 的创建移动到单独的类并将委托(delegate)和数据源从 VC 移动到单独的类来精简我的 ViewController 时遇到了一些
我是一名优秀的程序员,十分优秀!