集成测试EF5(或EF4x)

Jer*_*ose 2 c# integration-testing unit-testing entity-framework asp.net-mvc-3

在阅读了关于单元测试实体框架的几个问题/答案后,我决定放弃进行集成测试的单元测试.我认为与EF上下文交互是一种"私有"行为,因为它不需要独立于我的服务进行单元测试,也不能轻易准确地进行模拟.

注意:在我的示例中,我使用的是EF5.

首先,我有一个创建用户的服务方法:

void CreateUser(string username, string password);
Run Code Online (Sandbox Code Playgroud)

我的测试程序集有一个SetUpFixture(一次性用于测试运行),它创建了我的数据库(EF Code First)和测试数据:

[SetUpFixture]
public class SetUpFixture
{
    [SetUp]
    public void SetUp()
    {
        using (var context = new MyDbContext())
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());

            // Set up a bunch of initial data and commit
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在每次测试之前,运行我的TestFixtureSetup方法创建我的数据库上下文的实例,该实例在处理时(在每次测试之后)设置为回滚,并且还创建我的服务实例:

[TestFixtureSetUp]
public virtual void TestFixtureSetUp()
{
    _context = new MyContext(rollbackOnDispose: true);
    UserService = new SignupService(_context);
}

[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
    Context.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

最后,我的实际集成测试是为了确保传入了哪些有效数据,创建了一个带有我的用户名的记录(这里是我的问题所在):

[Test]
public void ValidDataShouldResultInNewRecordWithUsername()
{
    SignupService.CreateUser("myuser", "fakepassword");

    var q = from user in Context.Users 
            where user.Username == "myuser"
            select user;

    var actualUser = q.Single();

    Assert.AreEqual("myuser", actualUser.Username);
}
Run Code Online (Sandbox Code Playgroud)

这是我的问题:

1)首先,这甚至是依赖EF测试服务的方法吗?我知道有几种方法,我只是想确保这种方法没什么可疯狂的.

2)其次,我应该如何验证服务方法(CreateUser)在提交数据之前做了它应该做的事情(我不希望它提交,因此我的数据库状态保持不变,因为它在之前被初始化每个测试)?上面测试中的查询不返回任何数据,因为它尚未提交.

Eri*_*k T 6

对于#2,如果您使用的是SQL Server,则可以使用快照.您在加载测试数据时创建快照.然后,您运行测试,并在拆解(或您使用的任何后测试方法),您还原到快照.恢复快照非常快,因此它是测试数据库的实用方法.

我已经习惯了另一种方法取得了一些成功.不使用SQL数据库,而是使用SQLCE数据库.然后,您可以将测试数据作为文件进行管理 - 无论如何您都在创建数据库.我更喜欢快照方法,但它们都有效.

对于#1,这是一个集成测试.在测试应用程序的数据部分时,访问数据库是正确的做法.抽象出一个存储库只会给你带来更多的复杂性,因为你需要针对数据库测试存储库.

祝你好运,埃里克