针对 Entity Framework InMemory 进行测试

Testing against Entity Framework InMemory

我目前正在使用内存数据库测试Entity Framework的DbContext

为了使测试尽可能原子化,DbContext 每个测试方法都是唯一的,并且填充了每个测试所需的初始数据。

为了设置 DbContext 的初始状态,我创建了一个 void SetupData 方法,用我将在测试中使用的一些实体填充上下文。

这种方法的问题是测试无法访问在设置期间创建的对象,因为 Entity Framework 将分配 ID 本身,直到 运行 时间才知道.

为了克服这个问题,我认为我的 SetupData 方法可以变成这样:

public Fixture SetupData(MyContext context) 
{
    var fixture = new Fixture();
    fixture.CreatedUser = new User();
    context.Users.Add(fixture.CreatedUser);
    context.SaveChanges();

    return fixture;
}

public class Fixture 
{
    public User CreatedUser { get; set;}
}

如您所见,它是 return 一个我称之为 "Fixture" 的实例。 (不知道这个名字适不适合).

这样,SetupData 将 return 对象 (Fixture) 引用实体 。因此,测试可以使用创建的对象。否则,对象将无法识别,因为在调用 SaveChanges 之前不会创建 Id。

我的问题是:

我更喜欢这种方法:

public void SetupData(MyContext context) 
{
    var user = new User() { Id = Fixture.TEST_USER1_ID, UserName = Fixture.TEST_USER1_NAME };
    context.Users.Add(user);
    context.SaveChanges();
}

public class Fixture 
{
    public const int TEST_USER1_ID = 123;
    public const string TEST_USER!_NAME = "testuser";
}

您的方法可能也不错,但您可能想知道测试中某处的用户 ID,这使得在单个已知位置指定它变得非常容易,这样它就不会改变例如,您稍后更改测试数据和添加用户的顺序。

这不是一个坏习惯。事实上,这是创建可读的 Given-When-Then 测试的好方法。如果您考虑:

  • 拆分您的 SetupData 方法
  • 重命名
  • 可能会更改为扩展方法
public static MyContextExtensions
{
    public static User Given(this MyContext @this, User user)
    {
        @this.Users.Add(user);
        @this.SaveChanges();

        return user;
    }

    public static OtherEntity Given(this MyContext @this, OtherEntity otherEntity)
    {
         // ...
    }

    // ...
}

然后你可以写(一个概念性的例子,细节需要修改以匹配你的实现):

[Test]
public GivenAUser_WhenSearchingById_ReturnsTheUser()
{
    var expectedUsername = "username";
    var user = _context.Given(AUser.WithName(expectedUsername));

    var result = _repository.GetUser(user.Id);

    Assert.That(result.Name, Is.EqualTo(expectedUsername));
}

...其他实体也是如此。