【问题标题】:Testing Interactor methods that do more than one thing in a Clean architecture测试在 Clean 架构中做不止一件事的交互器方法
【发布时间】:2018-08-27 14:37:30
【问题描述】:

我一直在阅读有关单元测试和Clean architecture 的内容,并尝试实现涉及这两件事的内容。

据我了解,Clean 架构的结构是为了对 Interactor 对象的方法进行单元测试。

但是当用例类似于“创建一个文件,其内容是从某种格式的某些数据中计算出来的”时,我会感到困惑,因为它不是单一的(有文件内容的计算和文件的创建,这两个都是用例)

这里有一些伪代码说明我的情况:

/* We are in an Interactor (i.e. UseCaseObject)
 * This method 1)computes fileContent and 2)writes it into a file. 
 */
public void CreateFileFromData(someDataInSomeFormat) {
    var parsedData = SomeParser.Parse(someDataInSomeFormat);

    string fileContent = ???; 

    WriteFile(fileContent); 
}

我的问题如下:

  1. Interactor 中定义的方法必须是单一的吗? (例如,只做一件事)
  2. 必须对交互器中定义的方法进行单元测试吗? (我看到一个函数,不管是不是单一的,作为一个可测试的单元,如果不正确,请纠正我)
  3. 哪个类必须在 Clean 架构中进行 fileContent 的计算?

【问题讨论】:

    标签: unit-testing clean-architecture


    【解决方案1】:

    你没有告诉计算数据将从哪里“加载”,但例如让我们假设数据将从另一个文件中读取。

    您的交互器将具有三个依赖项
    - 读取文件
    - 计算新文件的数据
    - 写入文件

    public class Interactor
    {
        public Interactor(IReader reader, ICalculator calculator, IWriter writer)
        { }
    
        public void DoJob()
        {
            var data = reader.Read();
            var calculatedData = calculator.Calculate(data);
            writer.Write(calculatedData);
        }
    }
    

    使用这种方法Interactor 将负责“组合”完成任务所需的步骤。

    您可以通过模拟所有依赖项来简单地测试 Interactor。

    其中:
    IReaderIWriter网关
    ICalculatorInteractor 使用的UseCase 的实现细节 p>

    Interactor 中定义的方法必须是单一的吗? (如,只做 一件事)

    方法应该做一件事——执行用例相关的任务。如果任务需要使用网关(外部资源)或任务很复杂以将其保存在一种方法中 - 您将引入所有必需的单元作为依赖项,交互者的责任是将它们“粘合”在一起。

    必须对 Interactor 中定义的方法进行单元测试吗? (我看到一个 功能,统一与否,作为可测试单元,请纠正我 这是不正确的)

    仅抽象网关(外部资源) - 然后您可以测试交互器的整个逻辑。如果您首先编写测试 - 您将编写测试并且整个逻辑可以在一个函数中(它可能/应该是丑陋的意大利面条代码,这使得测试通过)。然后,当您看到实施的全貌后,您就可以开始将员工转移到专门的课程中。

    哪个类必须在 Clean 中保存 fileContent 的计算 架构?

    如果是简单的一行计算,它可以是交互器。但我更喜欢引入专门的计算类并将其作为依赖项引入。虽然测试将保留在交互器中,但专用计算类将通过交互器测试进行测试

    【讨论】:

    • 感谢您的回答。但是,它没有提及 Clean 架构,我的问题是专门针对该架构的。您提到“将在哪里加载数据”这一事实似乎表明您的答案超出了 Clean 架构的范围,因为后者通过诸如“实体”和“网关”等已识别的概念来处理此类事情”。您能否更清楚地说明您的答案如何适合清洁架构?
    • 谢谢!关于我遇到的困难的更多信息:) 最后一件事:即使你回答了我的第三个问题,你能否也为未来的读者澄清你的答案中整个 3 个问题的答案?根据我的理解应该是:[1:No][2:Yes][3:an implementation detail of the Interactor implementation an interface] 再次感谢!
    • 转念一想,在阅读this 之后,似乎一个好的单元测试是一个如果我更改函数内部也不会中断的测试。据我了解,您的方法将创建一个测试,该测试会在我决定更改交互器函数内部的那一刻中断! (请参阅我的链接中提供的非常相似的示例)
    • @Minh-TâmTRAN - 检查更新的答案。您只需要抽象(模拟)使测试变慢的依赖项(外部资源)。
    • 我在这个线程中所读到的内容帮助了我并教会了我很多东西(导致花了 2 天时间阅读各种内容),事后看来,我找到了我的答案问题。在输入我自己的答案时,我意识到它都包含在您的答案中。很抱歉,我的想法花了很长时间才变得足够成熟,才能像现在这样阅读您的答案。我将您的答案标记为答案:)
    【解决方案2】:

    Clean Architecture 的一个核心方面是所有应用程序业务逻辑都在 Interactor 方法中。这意味着您还希望将主要测试重点放在通常使用单元测试和低级验收测试的交互器上。

    在设计你的交互器方法时,你仍然应该遵循 SRP:应该只有一个改变的理由。 你也可以结合Interactors来跟随SRP。

    如果文件内容的计算是你的应用程序业务逻辑,它应该在一个 Interactor 方法中。

    有关交互器的更详细讨论,请查看我的帖子:https://plainionist.github.io/Implementing-Clean-Architecture-UseCases/

    【讨论】:

    • 出于建设性目的,现在绝对不是关于您的答案的反馈,而是关于我的问题的反馈我找到了我的问题的答案(非常感谢您在此处和您的文章中的所有链接),我意识到实际上我将 WriteFile() 视为我的用例的主要和完成步骤,它让我重新考虑在哪里进行计算;而实际上它应该是相反的,WriteFile 代码必须从交互器中移开。显然,您无法猜到所有这些,我只是这样说,以防它可以帮助您发现其他帖子中的真正问题:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-04
    • 2020-03-09
    • 2022-01-13
    • 1970-01-01
    • 2017-09-16
    • 1970-01-01
    • 2017-10-03
    相关资源
    最近更新 更多