我目前正在使用内存数据库测试实体框架 DbContext
。
为了使测试尽可能原子化,DbContext
每个测试方法都是唯一的,并且它填充了每个测试所需的初始数据。
要设置DbContext
的初始状态,我创建了一个void SetupData
方法,用我将在测试中使用的某些实体填充上下文。
这种方法的问题在于,测试期间无法访问在安装过程中创建的对象,因为实体框架将分配自身的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;}
}
如您所见,它返回了我称之为" Fixture" 的实例。 (我不知道名字是否合适)。
这样,SetupData
会返回一个对象(Fixture
),并引用实体。因此,测试可以使用创建的对象。否则,该对象将无法识别,因为在调用SaveChanges
之前不会创建Id。
我的问题是:
答案 0 :(得分:2)
我更喜欢这种方法:
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,这使得在单个已知位置指定它非常容易,以便在例如您不会改变时稍后更改您的测试数据和添加用户的顺序。
答案 1 :(得分:1)
这不是一个坏习惯。实际上,创建可读的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));
}
......和其他实体类似。