坚持复杂的测试数据

时间:2009-09-24 12:47:45

标签: testing persistence fluent-interface test-data

我们正在使用构建器模式来生成测试数据。这些域对象之间存在关系。我们的功能测试要求保留这些对象。

想想这个模型:

domain model

如果我想要一个简单的C实例,我会aNew().c().build()

如果我希望它被保留,我会aNew().c().saveIn(session)

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

嗯,你明白了。我的问题是,如果我想坚持一个C,它应该坚持它的B吗?或者它应该在手前坚持下去?如果我想要一个合理的默认B呢?如果我想坚持D怎么样?它应该坚持所有的A,B,C吗?

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

编辑:看起来我遇到了语言障碍,我的母语不是英语,所以我很抱歉默默无闻。以下是更多信息:

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

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

  1. 跟踪构建实体时明确设置的内容。
  2. 假设显式设置的实体已经保留,请不要保留它们。
  3. 坚持其他一切(用他们自己的毅力)。
  4. 这样可行,但我的蜘蛛感觉刺痛,我觉得我做错了,因为测试代码会涉及到逻辑,没有测试就很难处理。

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

    但是当我试图坚持我的测试数据时,如果没有它的关系,hibernate将不会让我保存一个实体。

    我该如何克服这个问题?

6 个答案:

答案 0 :(得分:3)

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

  

我的问题是,如果我想坚持一个C,它应该坚持它的B吗?或者它应该在手前坚持下去?

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

答案 1 :(得分: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,则需要考虑这些问题。

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

就个人而言,我喜欢在DALs中使用NHibernate进行交易时的存储库模式。我使我的存储库从IDisposable实现,让他们分别获得一个会话。通过这种方式,您可以将“工作单元” - 模式添加到您的设计中。

祝你好运:)

答案 2 :(得分:1)

我按主题分开你的答案。

  

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

这完全取决于您选择强制执行的域限制。例如, C 是一个实体而 B 是一个值对象?换句话说, C 是否具有独特的身份和生命? B 主要通过其价值确定,其生命周期与其父 C 紧密相关吗?

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

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

  

还是应该在手前坚持?

要回答这个问题,您可能需要映射出对象依赖关系。当对象图持久化到RDBMS时,这些依赖关系通常由外键约束表示。如果 C 在没有引用 B 的情况下无法运行,那么您可能希望在事务中保留它们,首先完成 B 遵守数据库的外键约束。按照上述思路,如果 B C 的子实体或值对象,您甚至可能 C 负责持久即可。

  

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

B 实例的创建可以委托给 B -Factory。是否将此工厂逻辑实现为类(非实例)方法,构造函数或将其作为自己的单元分离并不重要。关键是你有一个地方可以创建和配置新的 B 。在这个地方,您将拥有新实例化对象的默认配置。

Eric Evans {/ 3>提供了涵盖这些类型问题的优秀资源。{3}

答案 3 :(得分:1)

我不确定我是否理解您要解决的问题但是......如何使用XStream或Google的Protocol Buffers等序列化整个图表作为XML?

答案 4 :(得分:0)

  • 你的测试告诉你什么?
  • 这听起来像是在测试遗留应用程序吗?
  • 那么您的代码库中已经编写了功能并尝试创建覆盖测试吗?

请给我们更多反馈

答案 5 :(得分:0)

据我所知,问题在于您的域名(因为您已经绘制了它)。据我所知,C与B之间存在多对一的关系,数据库通过不可为空的外键字段对其进行创建。另一方面,从问题中的代码我可以理解,代码中没有强制执行一条规则,并且在C实例中引用B实例的成员可以为null。据我所知,域模型应该在代码和运行时始终是正确的,所以如果这个规则在代码中强制执行(例如,通过在C build()方法中要求B引用),你就不会有持久性的任何问题 - 你可以坚持所有。

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