2009-10-21 78 views
0

我是单元测试新手,认为我可能已经把自己挖到了一个角落。单元测试和主键

在你的单元测试中,处理主键的更好方法是什么?

希望一个例子会画出一些上下文。如果创建一个对象的多个实例(让我们说人)。

我的单元测试是测试正在创建的正确关系。

我的代码是创建荷马,他的孩子巴特和丽莎。他还有一个朋友巴尼,卡尔&莱尼。

我用接口分离了我的数据层。我的首选是保持主键简单。例如在保存时,Person.ProductID = new Random()。Next(10000);而不是说Barney.PersonID = 9110 Homer.PersonID = 3243等

主键是什么都没关系,它只需要是唯一的。

任何想法???

编辑:

对不起,我没有明确表态。我的项目设置为使用Dependency Injection。数据层是完全独立的。我的问题的焦点是,什么是实用的?

+1

您是否生成自己的主键?还是你让DB为你生成它们? – 2009-10-21 03:40:00

+1

数据库在插入时生成它 – 2009-10-21 03:54:07

回答

3

我有一个名为“唯一”的类,它产生独特的对象(字符串,整数等)。它通过保持一个内部静态计数器确保它们每次测试都是独一无二的。该计数器值每生成一个密钥都会增加,并以某种方式包含在密钥中。

所以当我设置了我的测试

var Foo = { 
    ID = Unique.Integer() 
} 

我喜欢这个,因为它传达的价值是不是这个测试,只是唯一重要的。

我有一个类似的'一些',不保证唯一性。当我需要测试的任意值时,我使用它。它对枚举和实体对象很有用。

这些都不是线程安全的或任何类似的东西,它严格的测试代码。

+0

Cha-ching!这就是我所追求的。谢谢! – 2009-10-21 21:43:15

0

为什么使用随机数?关键问题的数值?我只是在数据库中使用一个序列并调用nextval。

1

考虑使用GUID。它们在空间和时间上是独一无二的,这意味着即使两台不同的计算机在相同的时间点产生它们,它们也会不同。换句话说,他们保证是独一无二的。随机数字永远不会好,有相当大的碰撞风险。

可以使用静态类和方法生成GUID:

Guid.NewGuid(); 

假设这是C#。

编辑:

另一件事,如果你只是想产生大量的测试数据,而不必手工编写代码,或写循环一堆,看看NBuilder。开始使用可能有点困难(带有方法链接的Fluent方法对于可读性来说并不总是更好),但它是创建大量测试数据的好方法。

+0

使用Guid将会矫枉过正,而且我无法控制数据类型。但我会尝试NBuilder! – 2009-10-21 04:01:19

+0

使用Guids是商品数据库和廉价性能时代的新兴最佳实践。它不再过度杀伤。对于漂亮的URL来说太长了。 – yfeldblum 2009-10-21 04:24:42

+0

如果从int到Guid的转换的唯一好处是一个开发人员可以单元测试整个生产系统的一部分。那么是的,我认为这是过度杀伤 – 2009-10-21 04:38:02

3

有几个可能的角落,你可能挖到自己可能最终导致你问的问题。

  1. 也许你担心重复使用主键和覆盖或不正确地将数据加载中已有的数据库(比如,如果你反对,而不是一个干净的测试数据库开发数据库测试)。在这种情况下,我建议你设置你的单元测试,使用普通应用程序在干净,专用的测试数据库中测试的任何顺序创建记录的PK。

  2. 也许你担心你的代码与PK之外的效果超出简单的1,2,3。请放心,这不是通常在直接应用程序中测试的东西,因为大部分应用程序都不在应用程序的关注范围之内:从一个序列中生成一个数字是DB供应商的问题,跟踪内存中的数字是运行时/虚拟机的问题。

  3. 也许你只是想知道什么是这种事情的最佳做法。我建议你在执行测试用例之前插入记录,并使用应用程序本身将用于插入记录的相同工具建立数据库;大概你的应用程序代码将依赖数据库公开的PK序列号,如果是这样的话。最后,在执行完测试用例之后,测试应该回滚他们对数据库所做的任何更改,以确保多次执行测试为idempotent。这是我描述名为test fixtures的设计模式的抱歉尝试。

0

数据库单元测试的基本问题是主键不能被重用。相反,即使您使用原始密钥删除记录,每次创建新记录时,数据库都会创建一个新密钥。

有两种基本的方法来处理这个:

  1. 阅读生成的主键,从数据库,并在测试中使用它,或者
  2. 使用的数据库的新副本每次测试。

您可以将每个测试放入事务中,并在测试完成时将事务回滚,但回滚事务并不总是适用于主键;数据库引擎仍然不会重用曾经生成过的密钥(无论在SQL Server中)。

0

当通过另一段代码对数据库执行测试时,它不再是单元测试。它被称为“integration test”,因为您正在测试不同代码段的交互以及它们如何“集成”在一起。并不是说它真的很重要,但它很有趣。

当你进行测试时,应会发生以下情况:

  1. 开始一个数据库事务
  2. 插入已知的(可能是假的)测试项目/实体
  3. 调用(唯一一个)功能进行测试
  4. 测试结果
  5. 回滚事务

这些事情应该发生在每一个测试中。使用NUnit,你可以在第1步和第5步中只写一个基类,然后从每个测试类中继承。 NUnit将在基类中执行Setup和Teardown装饰方法。

在步骤2中,如果您使用的是SQL,则必须编写查询,以便将PK数字返回给您的测试代码。

INSERT INTO Person(FirstName, LastName) 
VALUES ('Fred', 'Flintstone'); 
SELECT SCOPE_IDENTITY(); --SQL Server example, other db vendors vary on this. 

然后你就可以做到这一点

INSERT INTO Person(FirstName, LastName, SpouseId) 
VALUES('Wilma', 'Flintstone', @husbandId); 
SET @wifeId = SCOPE_IDENTITY(); 

UPDATE Person SET SpouseId = @wifeId 
WHERE Person.Id = @husbandId; 
SELECT @wifeId; 

或其他任何你需要的。

在步骤4中,如果使用SQL,则必须重新选择数据并测试返回的值。

如果你足够幸运能够像(N)Hibernate(或其他)一样使用像样的ORM,步骤2和步骤4就不那么复杂了。

+0

我不需要测试数据库。我知道那作品 – 2009-10-21 21:11:26