gpt4 book ai didi

php - 我如何构建我的类以便更轻松地进行单元测试?

转载 作者:IT王子 更新时间:2023-10-29 01:16:48 24 4
gpt4 key购买 nike

我承认,我没有进行太多的单元测试……但我愿意。话虽如此,我有一个非常复杂的注册过程,我想对其进行优化以简化单元测试。 我正在寻找一种方法来构建我的类,以便我将来可以更轻松地测试它们。 所有这些逻辑都包含在 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 验证过程。其结果存储在本地。

  • 这是一个非常简单的示例,因为实际代码依赖于至少 6 个外部 API 调用和注册期间创建的 10 多个本地数据库行。在构造函数级别使用依赖注入(inject)是没有意义的,因为我可能需要在 Controller 中实例化 6 个类,而不知道我是否都需要它们。我希望完成的事情类似于 Provider::create('external')我只是指定开始注册的起始步骤。

    问题的关键

    如您所见,这只是注册过程的一个示例。我正在构建一个系统,我可以在其中拥有数百个服务提供商(外部 API 模块),我需要注册、更新、删除等。这些提供商中的每一个都与用户帐户相关联。

    我想以一种可以在触发创建新提供程序时指定操作顺序(步骤)的方式来构建这个系统。换句话说,允许我指定在事件链中接下来触发哪个提供程序/方法组合,因为创建可以跨越这么多步骤。目前,我通过主题/观察者模式发生了这一系列事件。我希望将此代码移动到数据库表中, provider_steps ,我在这里列出了每个步骤以及它后面的 success_stepfailure_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')

    这里有很多决定要做……我知道我应该更喜欢组合而不是继承并创建一些接口(interface)。我也知道我可能需要工厂。最后,我这里有很多领域模型的问题……所以我可能需要业务领域类。我只是不知道如何将它们全部融合在一起,而不会在我追求 chalice 的过程中造成一团糟。

    另外,进行数据库查询的最佳位置在哪里?

    我已经为每个数据库表创建了一个模型,但我很想知道在哪里以及如何实例化特定的模型方法。

    我读过的东西...
  • Design Patterns
  • The Strategy Pattern
  • Composition over Inheritance
  • The Factory method pattern
  • The Abstract factory pattern
  • The Builder pattern
  • The Chain-of-responsibility pattern
  • 最佳答案

    您已经在使用 pub/sub 模式,这似乎很合适。除了您上面的评论外,我会考虑将有序列表作为优先级机制。

    但是,每个订阅者都关心其依赖项的操作顺序以触发成功/失败,这仍然令人不快。依赖项通常看起来像是属于一棵树,而不是一个列表。如果您将它们存储在树中(使用复合模式),那么内置递归将能够通过首先清理其依赖项来清理每个依赖项。这样您就不必再担心清理发生的优先顺序 - 树会自动处理。

    您可以使用树来存储发布/订阅订阅者,几乎就像使用列表一样容易。

    使用测试驱动的开发方法可以满足您的需求,并确保您的整个应用程序不仅完全可测试,而且完全被测试覆盖,证明它可以满足您的需求。我首先会准确描述您需要做什么才能满足一个要求。

    您知道自己想要做的一件事是添加一个提供程序,因此 TestAddProvider() 测试似乎是合适的。请注意,此时它应该非常简单,并且与复合模式无关。一旦它起作用,您就会知道提供者有一个依赖项。创建一个 TestAddProviderWithDependent() 测试,看看效果如何。同样,它不应该很复杂。接下来,您可能想要 TestAddProviderWithTwoDependents(),这就是列表将被实现的地方。一旦它起作用,您就知道您希望 Provider 也是一个 Dependent,因此新的测试将证明继承模型有效。从那里,您将添加足够的测试以说服自己添加提供者和依赖项的各种组合有效,并测试异常条件等。仅从测试和需求中,您将快速得出满足您需求的复合模式.在这一点上,我实际上会打开我的 GoF 副本,以确保我了解选择复合图案的后果,并确保我没有添加不合适的疣。

    另一个已知的要求是删除提供者,因此创建一个 TestDeleteProvider() 测试,并实现 DeleteProvider() 方法。您也不会远离提供程序删除其依赖项,因此下一步可能是创建 TestDeleteProviderWithADependent() 测试。复合模式的递归在这一点上应该是显而易见的,您应该只需要再进行几次测试就可以说服自己深嵌套的提供者、空叶子、宽节点等,所有这些都会正确地清理自己。

    我认为您的提供商需要实际提供他们的服务。是时候测试调用提供程序(使用模拟提供程序进行测试),并添加测试以确保它们可以找到它们的依赖项。同样,复合模式的递归应该有助于构建依赖项列表或正确调用正确提供程序所需的任何内容。

    您可能会发现必须按特定顺序调用提供程序。此时,您可能需要为复合树中每个节点的列表添加优先级。或者,您可能必须构建一个完全不同的结构(例如链表)才能以正确的顺序调用它们。使用测试并慢慢接近它。您可能仍然有人担心您以特定的外部规定顺序删除家属。在这一点上,您可以使用您的测试向怀疑者证明您将始终安全地删除它们,即使不是按照他们所想的顺序。

    如果你一直做对了,你之前的所有测试都应该继续通过。

    然后是棘手的问题。如果您有两个共享公共(public)依赖项的提供程序怎么办?如果您删除一个提供程序,它是否应该删除其所有依赖项,即使不同的提供程序需要其中之一?添加测试,并实现您的规则。我想我会通过引用计数来处理它,但也许您想要第二个实例的提供者的副本,所以您永远不必担心共享子项,并且您可以通过这种方式使事情变得更简单。或者也许这在您的域中从来都不是问题。另一个棘手的问题是您的提供者是否可以具有循环依赖关系。您如何确保不会陷入自我参照循环?编写测试并弄清楚。

    在您弄清楚整个结构之后,您才会开始考虑用于描述此层次结构的数据。

    这就是我会考虑的方法。它可能不适合您,但由您决定。

    关于php - 我如何构建我的类以便更轻松地进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16826582/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com