坚持复杂的测试数据

问题描述 投票:7回答:6

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

想想这个模型:

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. 坚持一切(用他们自己的毅力)。

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

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

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

我怎样才能克服这个问题?

testing persistence fluent-interface test-data
6个回答
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实现,让他们分别获得一个会话。通过这种方式,您可以将“工作单元” - 模式添加到您的设计中。

祝你好运:)


3
投票

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

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

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


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

埃里克·埃文斯(Eric Evans)的Domain-Driven Design是一个涵盖这些类型问题的优秀资源


1
投票

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


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

请给我们更多反馈


0
投票

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

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

推荐问答
热门问答
最新问答