8

我们正在使用构建器模式来生成测试数据。这些领域对象之间有关系。我们的功能测试要求这些对象被持久化。

想想这个模型:

领域模型

如果我想要一个普通的 CI 实例aNew().c().build()

如果我想让它坚持下去,我会aNew().c().saveIn(session)

如果我想要一个具有已知 BI 的 C 实例aNew().c().with(b).build()

嗯,你明白了。我的问题是,如果我想坚持一个C,它应该坚持它是B吗?还是应该事先坚持?如果我想要一个合理的默认 B 怎么办?如果我想坚持 D 怎么办?它应该坚持所有A,B,C吗?

当然,真实系统要复杂得多(有时带有循环引用)。我正在寻找持久化复杂测试数据的最佳实践。

编辑:看来我遇到了语言障碍,我的母语不是英语,所以我很抱歉晦涩难懂。以下是更多信息:

  • 我要测试的不是遗留代码
  • 我正在尝试编写覆盖测试,而不是单元测试(因此我不会嘲笑任何东西)
  • 如果数据库被填充到一定程度(它不使用所有实体),我试图测试的软件就可以工作。

PS。请不要犹豫,询问更多信息,因为我一直在努力寻找可能的最佳实践。我想出的最接近的事情是:

  1. 在构建实体时跟踪明确设置的内容。
  2. 假设显式设置的实体已经被持久化,不要持久化它们。
  3. 坚持其他一切(使用他们自己的坚持者)。

这会起作用,但是我的蜘蛛感觉很刺痛,我认为我做错了什么,因为测试代码中会涉及到逻辑,如果没有测试,处理起来会非常复杂。

编辑2:我会尽量让自己更清楚。当我编写/运行我的单元和一些集成测试时,我没有问题,因为测试数据没有持久化,它存在于内存中。

但是当我尝试持久化我的测试数据时,休眠不会让我保存没有关系的实体。

我该如何克服这个问题?

4

6 回答 6

3

您可能应该更详细地描述您的测试设置。特别是,为什么您的功能测试需要持久化这些对象?你在测试实际的持久化操作吗?或者这只是运行测试的副作用?您想在测试中加载持久对象吗?

我的问题是,如果我想坚持一个C,它应该坚持它是B吗?还是应该事先坚持?

这将取决于您首先坚持的原因。如果您正在集成测试持久层,那么您应该只使用应用程序本身使用的逻辑。如果这只是测试的副作用,您可能想要模拟持久层等......

于 2009-09-24T13:03:33.650 回答
1

我按主题将您的答案分开。

我的问题是,如果我想坚持一个C,它应该坚持它B吗?如果我想坚持 D 怎么办?它应该坚持所有A,B,C吗?

这完全取决于您选择强制执行的域约束。例如,C是实体,B是值对象吗?换句话说,C有自己独特的身份和生命吗?B是否主要通过它的价值和它的生命周期与其父C的生命周期紧密耦合来识别?

提出这些类型的问题应该有助于指导您决定坚持什么、何时以及由谁坚持。

例如,如果CB都是仅共享关系的实体,您可能会决定独立地保留它们,因为可以想象每个人都可以拥有自己的有意义的生活和身份。如果B是一个值对象,您可能会选择让其父实体C控制其生命,包括对象的创建/检索/更新/删除。这很可能包括C持久化B

还是应该事先坚持?

要回答这个问题,您可能必须绘制出您的对象依赖关系。当对象图被持久化到 RDBMS 时,这些依赖关系通常由外键约束表示。如果C没有对B的引用就无法运行,那么您可能希望将它们都保存在事务中,首先完成B以符合数据库的外键约束。按照上面的思路,如果B是C的子实体或值对象,您甚至可能让C负责持久化B

如果我想要一个合理的默认 B 怎么办?

B实例的创建可以委托给B -Factory。无论您是将此工厂逻辑实现为类(而非实例)方法、构造函数,还是将其作为自己的单元分离出来,都无关紧要。关键是你有一个地方可以创建和配置新的B。正是在这个地方,您将对新实例化的对象进行默认配置。

涵盖这些类型问题的优秀资源是Eric Evans的领域驱动设计

于 2009-09-30T14:27:57.993 回答
1

我不确定我是否理解您要解决的问题,但是......使用XStream或 Google 的Protocol Buffers之类的东西将整个图序列化为 XML怎么样?

于 2009-10-03T21:48:36.473 回答
1

您需要更好地定义域上的级联。如果您无法对其进行测试,您希望它在实际应用中的表现如何?

例如:

A -> B:这段关系的主人是谁?您想将 B 添加到 A,还是相反?这可以是一个实现细节,您可以同时拥有 B.SetParent(A) 和 A.Children.Add(B),并且在 A.Children.Add(B) 的情况下将 B 的父级设置为 A(与另一个类似)方式)。如果你这样做会发生什么:

A a1 = new A();
A a2 = new A();
B b = new B();
a1.Children.Add(b);
b.SetParent(a);

你需要在这里下定决心。没有一个解决方案是完美的,所以这里适用的基本上是个人喜好和应用程序的一致性。

使用 ORM 会比使用纯 SQL(或任何其他数据源,如 XML 或您自己的数据源)更快地解决这些约束问题,但如果您也要编写纯 SQL,则需要考虑这些问题。

对不起,我没有给你一个明确的答案,但在我看来,你需要考虑一些(我认为)你还没有完成的限制。

就个人而言,我喜欢在 DAL 中使用 NHibernate 时的存储库模式。我让我的存储库从 IDisposable 实现,并让它们各自获得一个会话。通过这种方式,您可以将“工作单元”模式融入您的设计。

祝你好运:)

于 2009-09-30T14:13:40.283 回答
0

据我所知,问题出在您的域(正如您所绘制的那样)。据我了解,C 与 B 具有多对一的关系,并且数据库通过不可为空的外键字段来强制它。另一方面,从问题中的代码中,我可以理解代码中没有强制执行完全一致的规则,并且在 C 实例中引用 B 实例的成员可以为空。据我了解,域模型在代码和运行时应该始终正确,因此如果在代码中强制执行此规则(例如通过在 C build() 方法中要求 B 引用),您将不会持久性的任何问题 - 你可以坚持所有。

其他更肮脏的解决方案只是在测试之前以编程方式删除所有与您的测试混淆的数据库约束并在之后恢复它们。当然,这会使数据库完全无法用于并行运行的任何其他测试,但这可以通过仅用于测试的 SQLite 或 SQL Server Compact Edition 等交互数据库来解决。

于 2009-10-04T22:14:02.577 回答
0
  • 你的测试告诉你什么?
  • 这听起来像是您在测试遗留应用程序?
  • 所以你的功能已经写在你的代码库中并试图创建一个覆盖测试?

请给我们更多反馈

于 2009-09-24T16:04:00.507 回答