在阅读了 Uncle Bob 博客和 TSS id DEAD 文章后,我想编写更多测试代码。
我的应用程序正在使用层:ApplicationService -> Model -> Support。
应用服务定义用例。这是一些程序代码(loadX、调用 X.doSomething、saveX)。在某些情况下,appService 类需要很多外部依赖项。
例如,我在“工作流程”的末尾使用了一个对象“D”。因此,在使用它时,我需要检查与此链接的对象“A”/“B”/“C”的一些规则。假设我们有 3 个相关的实体,每个实体都有自己的存储库,在非常特殊的用例中,我需要更深入地检查另一个规则(比如“AA”== 另一个存储库)。
我的 appService 将有大约 8 到 10 个依赖项(5 个存储库 + 一些业务服务实体间)。测试不是我的“强项”。 mock 太多服务时缺乏勇气。如果每个服务有 3 个方法但我的 appService 只需要一个,那么我需要知道模拟或模拟/ stub 所有方法的方法(以及模拟/ stub 2/3 的无用方法)。
我认为有很多问题。我的第一个是太多的依赖。我只是不想拆分依赖项并将它们隐藏在地毯下。当然,领域实体也可能存在问题:边界错误、分离过多等。
阅读 article (uncle bob) 之后,我在想:我做错了。也许 appService 需要表达/声明它需要的接口(interface)。但是怎么命名呢?怎么拆分呢?我不确定将不相关的服务放在一起是个好主意。编写类似外观的东西,测试会变得更容易(我现在知道我的类与这些外部服务耦合,而且我确切地知道是哪些)。
- 您如何测试您的应用服务? (仅单元或集成测试?)
- 您认为拆分注入(inject)的服务(“门面”之类的或更业务的或其他的)是一种好方法吗?
谢谢
更新 1(2016 年 6 月 23 日): 例如,这里是应用程序服务类 DPAppService 的依赖项列表:
DPRepository dpRepository;
RechercheFournisseurQueryService rechercheFournisseur;
RechercheFactureQueryService rechercheFacture;
DateService dateService;
SFRepository sfRepository;
CommandeRepository commandeRepository;
RepartitionBudgetRepository repartRepo;
GenerateurRepartitionsDPService genRepartDpService;
SaisieRepartitionsSurDpQueryService saisieRepartitionsSurDpQueryService;
ImputationBudgetaireService imputationBudgetaireService;
NomenclatureService nomenclatureService;
ComptaGeneraleService comptaGeneraleService;
OperationInfoService operationInfoService;
DemandePaiementCalculateurService dpCalculateurService;
CodeMarcheAnnualiseRepository codeMarcheAnnualiseRepository;
DemandePaiementLigneFactory dpLigneFactory;
EcritureRepository ecritureRepository;
BrouillardNonViseRepository brouillardNonViseRepository;
使用构造函数注入(inject)非常可怕,非常烦人。有一些存储库(我认为太多了,DP 服务只需要存储库公开的方法的一个子集),一些服务(目前我的域无法真正持有的计算)。
我需要更多地解释一下域。 此类负责附加到“DP”概念的所有用例(20 种方法,其中一些方法只是另一种方法的不同风格)。这个概念在“工作流”(这里使用的术语)的末尾:
- 首先用户创建一个“EJ”
- 然后他创建了一个“SF”
- 然后他创造了“Facture”
- 最后,他创建了一个 DP,将 previsous 对象绑定(bind)在一起,因此我们需要为某些用例检查/加载 previsous 对象之一。一个 DP 不能真正容纳所有的 previsous 对象(有点太多数据)。
想法:
- 我可以拆分用例(为每个子功能创建嵌套包)以限制依赖项的数量。
- 我可以重构模型
- 该类可以提供它需要的接口(interface)(我在 Bob 叔叔讲完关于 tdd/little architecture 的讲座后研究了这个想法)
任何优点/缺点/想法?
非常感谢您的时间/回答。
最佳答案
If each service has 3 methods but my appService only need one, then I need to know which method to mock or mock/stub all the methods (and mocking/stubbing 2/3 of useless methods).
是的,这是一种熟悉的代码味道;或者反过来——你已经有了工作测试,但你必须不断地回到他们那里去找出另一种方法,以保持测试的编译....
After reading an article (uncle bob), I was thinking : I'm doing it wrong. perhaps the appService will need to express / declare an interface with WHAT it needs.
是的。应用服务声明了某种服务提供者接口(interface)的风格,这为其提供了一种与实现无关的方式来描述它需要在其中运行的上下文。
如果有帮助,这类似于在您的域模型中使用存储库或域服务接口(interface)。
But how do name it ?
命名东西很烂:
- 背景
- 客户
- 连接器
- 网关
- 接缝
how to split it ? I'm not sure it's a good idea to put no related services together.
我已经开始将其视为聚合设计。我的意思是;我们通常通过想象模型中实体的结构来开始思考聚合。但是结构是一个真正静态的东西;域模型中重要的不是两个状态位通过结构连接,而是这些状态位的更改通过业务规则连接。
这在应用程序服务级别意味着什么?也就是说,如果应用服务支持多个用例,那么这告诉我们这些用例的共同点是什么?除了“以这种方式很容易实现”之外,应该还有一些动机。我自己在这里没有答案,但我认为这是要问的正确问题的方向。
How do you test your appServices ? (unit or integration test only ?)
两者都是,理想情况下。应用程序服务所在的模块具有测试,其中测试本身充当服务提供者。集成检查将多个模块连接在一起以提供服务,然后针对程序集运行一系列独立的测试。
Do you think splitting the injected services ("facade" like or something more business or something else) is a good approach ?
是的,但这并非没有代价。外观本身非常适合记录实际发生的事情。但是每个外观至少需要一个实现,可能更多(测试 stub 等)。您将在某个地方为此付出代价;在你的模块中更复杂的连接,寻找一些服务的实现实际存在的地方,试图找到名称类/包/模块以便你可以保持一切正常,等等。
关于unit-testing - DDD - 应用程序服务单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37940035/