如何使用 Sql CE 4 数据库进行功能测试
Posted
技术标签:
【中文标题】如何使用 Sql CE 4 数据库进行功能测试【英文标题】:How can I use Sql CE 4 databases for functional tests 【发布时间】:2011-01-13 03:16:07 【问题描述】:由于 Linq-to-Entities (EF4) 和 Linq-to-Objects 之间的潜在差异,我需要使用实际的数据库来确保我的查询类正确地从 EF 检索数据。 Sql CE 4 似乎是解决此问题的完美工具,但我遇到了一些小问题。这些测试正在使用 MsTest。
我遇到的问题是,如果没有重新创建数据库(由于模型更改),每次测试后数据都会不断添加到数据库中,而不会删除数据。这可能会导致测试冲突,查询返回的数据比预期的要多。
我的第一个想法是在TestInitialize
方法中初始化一个TransactionScope
,并在TestCleanup
中处理事务。不幸的是,Sql CE4 不支持事务。
我的下一个想法是通过File.Delete()
调用删除TestCleanup
中的数据库。不幸的是,这似乎在第一次测试运行后不起作用,因为第一次测试的TestCleanup
似乎删除了数据库,但第一次测试之后的每个测试似乎都没有重新创建数据库,因此它给出了一个错误找不到数据库文件。
我尝试将 TestInitialize
和 TestCleanup
标记更改为 ClassInitialize
和 ClassCleanup
用于我的测试课程,但是由于在 ClassInitialize
之前运行的测试导致 NullReferenceException
出错(或者这样出现。ClassInitialize
在基类中,所以可能是它的原因)。
我已经没有办法有效地使用 Sql CE4 进行测试。有没有人有更好的想法?
编辑:我最终找到了解决方案。在我的 EF 单元测试基类中,我启动了我的数据上下文的一个新实例,然后调用
context.Database.Delete()
和 context.Database.Create()
。单元测试运行速度稍慢,但现在我可以使用真实数据库有效地进行单元测试
最终编辑: 在与 Microsoft 来回发送一些电子邮件后,发现
TransactionScope
s 现在可以在 SqlCE 中使用最新版本的 SqlCE。但是,如果您使用的是 EF4,则存在一些限制,即您必须在开始事务之前显式打开数据库连接。以下代码显示了如何成功使用 Sql CE 进行单元/功能测试的示例:
[TestMethod]
public void My_SqlCeScenario ()
using (var context = new mysqlCeModelContext()) //ß derived from DbContext
ObjectContext objctx = ((IObjectContextAdapter)context).ObjectContext;
objctx.Connection.Open(); //ß Open your connection explicitly
using (TransactionScope tx = new TransactionScope())
var product = new Product() Name = "Vegemite" ;
context.Products.Add(product);
context.SaveChanges();
objctx.Connection.Close(); //ß close it when done!
【问题讨论】:
当然 SQL CE 支持事务...但是使用 TransactionScope 是一种非常错误的做法。只需通过 Connection 对象正常执行即可。 我不确定在没有TransactionScope
的情况下如何处理 EF4 实体,除非您的意思是不调用 SaveChanges()
,这意味着测试不是有效的测试。
您能否举例说明如何使用 Sql CE 播种数据?我使用 EF6 并想使用 sql ce 测试它
【参考方案1】:
在您的TestInitialize
中,您应该执行以下操作:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new System.Data.Entity.Database.DropCreateDatabaseAlways<YourEntityFrameworkClass>());
这将导致实体框架在运行测试时始终重新创建数据库。
顺便说一句,您可以创建一个继承自 DropCreateDatabaseAlways
的替代类。这将允许您每次都使用集合数据为您的数据库播种。
public class DataContextInitializer : DropCreateDatabaseAlways<YourEntityFrameworkClass>
protected override void Seed(DataContext context)
context.Users.Add(new User() Name = "Test User 1", Email = "test@test.com" );
context.SaveChanges();
然后在您的 Initialize 中,您将调用更改为:
System.Data.Entity.Database.DbDatabase.SetInitializer<YourEntityFrameworkClass>(
new DataContextInitializer());
【讨论】:
在终于有机会检查这个之后,这似乎不起作用。它似乎在运行任何测试之前删除了数据库,但在单独运行每个测试之前它并没有删除数据库。这导致数据在单元测试中持续存在,导致其他一些测试失败。我现在可以解决它,但我宁愿没有它。 没关系 - 它太复杂了,不适合单元测试......我基本上用它来将多个表映射到一个实体,每次我调用它时它都会在一个新的程序集中完成对于那个特定的表......真的很难解释:) 您不能在每次测试后清除表格并使用种子方法重新填充它们吗?还是每个测试都是一组不同的表? 一些测试我正在测试用户查询,一些我正在测试项目查询等。所以每个测试都使用不同的数据,所以我必须手动编写命令来清除每个表,之后每个测试,随着我的应用程序的增长,这似乎是很多艰苦的工作和维护,但也许这是唯一的方法(尽管这迫使我在我的存储库中公开 deleteAll 方法)。当我开始编写更多基于聚合的测试时(例如,在所有值中搜索关键字),我可以看到自己被测试之间的持久数据烧毁了。 我将您的帖子标记为解决方案,让您代表回来并给我一些建议。根据您的建议,我找到了一个更可靠的解决方案,我把它放在原帖中!谢谢!【参考方案2】:我发现“最终编辑”中的方法也适合我。然而,这真的很烦人。它不仅用于测试,还可以在任何时候将 TransactionScope 与 Entity Framework 和 SQL CE 一起使用。我想编写一次代码并让我的应用程序同时支持 SQL Server 和 SQL CE,但是在我使用事务的任何地方我都必须这样做。当然,Entity Framework 团队应该为我们处理这个问题!
与此同时,我更进一步,使其代码更简洁。将此块添加到您的数据上下文(无论您从 DbContext 派生的什么类):
public MyDataContext()
this.Connection.Open();
protected override void Dispose(bool disposing)
if (this.Connection.State == ConnectionState.Open)
this.Connection.Close();
base.Dispose(disposing);
private DbConnection Connection
get
var objectContextAdapter = (IObjectContextAdapter) this;
return objectContextAdapter.ObjectContext.Connection;
这会让你在实际使用时更干净:
using (var db = new MyDataContext())
using (var ts = new TransactionScope())
// whatever you need to do
db.SaveChanges();
ts.Complete();
虽然我认为如果您设计您的应用程序,以便在一次调用 SaveChanges() 中提交所有更改,那么隐式事务就足够了。对于测试场景,我们希望回滚所有内容,而不是调用 ts.Complete(),所以那里肯定需要它。我确信在其他情况下我们需要可用的事务范围。很遗憾,EF/SQLCE 不直接支持它。
【讨论】:
有趣的解决方案。我刚刚做了一个解决方法,我的IUnitOfWork
类有一个BeginTransaction()
和EndTransaction(bool commit)
方法。 BeginTransaction
将打开连接,如果它还不存在,EndTransaction
将允许我轻松完成或回滚事务。到目前为止,这似乎也有效。希望将来我可以自己使用事务范围。以上是关于如何使用 Sql CE 4 数据库进行功能测试的主要内容,如果未能解决你的问题,请参考以下文章