我是单元测试的新手,我想我可能已经把自己挖到了角落里。
在单元测试中,处理主键的更好方法是什么?
希望一个例子将描绘一些背景。如果创建一个对象的几个实例(让我们说人)。
我的单元测试是测试正在创建的正确关系。
我的代码是创建荷马,他的孩子巴特和丽莎。他还有一个朋友Barney,Karl&莱尼。
我用一个接口分隔了我的数据层。我的偏好是保持主键简单。例如,在保存时,Person.ProductID = new Random()。Next(10000);而不是说Barney.PersonID = 9110 Homer.PersonID = 3243等。
主键是什么并不重要,它只需要是唯一的。
任何想法???
修改
对不起,我还没说清楚。我的项目设置为使用Dependency Injection。数据层完全独立。我的问题的重点是,什么是实用的?
答案 0 :(得分:3)
你可能有几个可能的角落,最终会导致你问的问题。
也许您担心重新使用主键并覆盖或错误地加载已存在于数据库中的数据(例如,如果您正在针对dev数据库而不是干净的测试数据库进行测试)。在这种情况下,我建议您设置单元测试,以使用普通应用程序或在干净,专用的测试数据库中测试的顺序创建记录的PK。
< / LI>也许你担心你的代码在PK之后的功效超出了简单的1,2,3。请放心,这不是人们通常会在简单的应用程序中测试的东西,因为大部分内容都不在应用程序的关注范围内:从序列生成数字是数据库供应商的问题,跟踪内存中的数字是运行时/ VM的问题。
也许你只是想了解这类事情的最佳做法。我建议您在执行测试用例之前插入记录来设置数据库,使用与应用程序本身用于插入记录的相同工具;大概你的应用程序代码将依赖于数据库提供的PK序列号,如果是这样,请使用它。最后,在您的测试用例执行之后,您的测试应该回滚它们对数据库所做的任何更改,以确保测试在多次执行时idempotent。我很抱歉尝试描述名为test fixtures的设计模式。
答案 1 :(得分:3)
我有一个名为“Unique”的类,它产生唯一的对象(字符串,整数等)。它通过保持内部静态计数器确保它们是唯一的每次测试。该计数器值按生成的密钥递增,并以某种方式包含在密钥中。
所以当我设置我的测试时
var Foo = {
ID = Unique.Integer()
}
我喜欢这个,因为它传达的是这个值对于这个测试并不重要,只是唯一性。
我有一个类似的'Some'类,不保证唯一性。当我需要测试的任意值时,我会使用它。它对枚举和实体对象很有用。
这些都不是线程安全或类似的东西,它是严格的测试代码。
答案 2 :(得分:1)
考虑使用GUID。它们在空间和时间上是独一无二的,这意味着即使两台不同的计算机在同一时间生成它们,它们也会有所不同。换句话说,它们保证是独一无二的。随机数永远不会好,存在相当大的碰撞风险。
您可以使用静态类和方法生成Guid:
Guid.NewGuid();
假设这是C#。
修改强>
另一件事,如果您只想生成大量测试数据而无需手动编写代码或编写一堆for循环,请查看NBuilder。开始时可能有点困难(方法链的Fluent方法对于可读性并不总是更好),但它是创建大量测试数据的好方法。
答案 3 :(得分:0)
为什么要使用随机数?关键的数值是否重要?我只想在数据库中使用一个序列并调用nextval。
答案 4 :(得分:0)
数据库单元测试的基本问题是主键不会被重用。而是,每次创建新记录时,数据库都会创建一个新密钥,即使您使用原始密钥删除记录也是如此。
有两种基本方法可以解决这个问题:
您可以将每个测试放在一个事务中,并在测试完成时回滚事务,但回滚事务并不总是与主键一起使用;数据库引擎仍然不会重用已生成一次的密钥(无论如何都在SQL Server中)。
答案 5 :(得分:0)
当测试通过另一段代码对数据库执行时,它不再是单元测试。它被称为“integration test”,因为您正在测试不同代码片段的交互以及它们如何“集成”在一起。并不是真的很重要,但知道它很有趣。
执行测试时,应发生以下情况:
这些事情应该发生在每一次测试中。使用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就不那么复杂了。