伪造数据库时 Effort- FirstOrDefault returns null
Effort- FirstOrDefault returns null when Faking Database
我正在尝试为我的项目创建一些单元测试,经过大量挖掘后我发现了 Effort,这个想法很棒,它模拟数据库而不是处理伪造 DBContext ,顺便说一句,这真的很难在使用复杂模式时正确处理。
但是,在我专门将用户的电子邮件添加到由 Effort 创建的内存数据库中后,我试图获取该用户的电子邮件,这是代码
MyContext contextx = new MyContext(Effort.DbConnectionFactory.CreateTransient());
var client = new Client
{
ClientId = 2,
PersonId = 3,
Person = new Person
{
PersonId = 3,
EMail = "xxxxx@gmail.com"
}
};
contextx.Client.Add(client); //<-- client got added, I checked it and is there
var email = contextx.Client.Select(c => c.Person.EMail).FirstOrDefault();
在上面的最后一行中,我无法发送到 return 电子邮件 xxxx@gmail.com,而是它总是 returns null。
有什么想法吗?
回答您的直接问题
对于您提出的具体问题,我有两点建议:
看看 contextx.Client.ToArray()
看看你在那个集合中真正有多少成员。可能 Client
集合实际上是空的,在这种情况下你确实会得到 null。或者,可能是 Client 集合中的第一个元素具有 EMail
.
的空值
如果在查询 DbContext 上的 Client
集合之前调用 contextx.SaveChanges()
,行为会发生什么变化?我很好奇调用 SaveChanges
是否会导致新插入的值存在于集合中。这确实不是必需的,但 Effort 和 DbContext
.
之间可能存在一些奇怪的相互作用
编辑: SaveChanges()
结果就是答案。
一般测试建议
既然你用 "unit-testing" 标签标记了这个问题,我将根据我作为单元测试从业者和教练的十年经验提供一些一般的单元测试建议。 单元 测试是关于单独测试应用程序的各个小部分。通常这意味着单元测试一次只与几个 类 交互。这也意味着单元测试不应依赖于外部库或依赖项(例如数据库)。相反,集成 测试一次测试系统的更多部分,并且可能对数据库等事物具有外部依赖性。
虽然这看起来像是对术语的质疑,但这些术语对于将测试的实际意图传达给团队的其他成员很重要。
在这种情况下,要么您真的想对碰巧依赖于 DbContext 的某些功能进行单元测试,要么您正试图测试您的数据访问层。如果您尝试编写一个直接依赖于 DbContext 的独立单元测试,那么您需要打破对 DbContext 的依赖。我将在下面的 打破对 DbContext 的依赖性中对此进行解释。否则,您实际上是在尝试集成测试您的 DbContext,包括您的实体是如何映射的。在这种情况下,我总是发现最好隔离这些测试并使用真实的(本地)数据库。您可能希望使用本地安装的与您在生产中使用的数据库种类相同的数据库。通常,SqlExpress 工作得很好。将您的测试指向一个数据库实例,测试可以完全破坏该实例。让您的测试在每次测试 运行 之前删除任何现有数据。然后,他们可以设置他们需要的任何数据,而不用担心现有数据会发生冲突。
打破对 DbContext 的依赖
那么,当您的业务逻辑依赖于访问 DbContext
时,您如何编写好的单元测试? 你没有。
在我使用 Entity Framework 进行数据持久化的应用程序中,我确保对 DbContext
的访问包含在一个单独的数据访问项目中。通常,我会创建 类 来实现 Repository 模式,并且允许那些 类 依赖于 DbContext
。因此,在这种情况下,我将创建一个实现 IClientRepository
接口的 ClientRepository
。界面看起来像这样:
public interface IClientRepository {
Client GetClientByEMail(string email);
}
然后,任何需要访问该方法的 类 都可以使用基本存根/模拟/其他任何方式进行单元测试。不用担心 mocking DbContext
。您的数据访问层已包含在内,您可以使用真实数据库对其进行全面测试。有关如何测试数据访问层的一些建议,请参阅上文。
作为一个额外的好处,此接口的实现定义了在一个统一的地方通过电子邮件地址查找 Client
的含义。 IClientRepository
界面让你快速答题,"How do we query for Client
entities in our system?"
依赖于 DbContext
与允许域 类 依赖于连接字符串并且到处都有 ADO.Net 代码的测试问题的规模大致相同。这意味着您必须创建一个包含真实数据的真实数据存储(即使使用假数据库)。但是,如果您将对 DbContext
的访问包含在特定的数据访问程序集中,您会发现编写单元测试要容易得多。
就项目组织而言,我通常只允许我的数据访问项目引用 Entity Framework。我将有一个单独的核心项目,我在其中定义实体。我还将在 Core 项目中定义数据访问接口。然后,具体的接口实现被放入数据访问项目中。然后,您解决方案中的大多数项目都可以简单地依赖核心项目,只有顶级可执行文件或 Web 项目真正需要依赖数据访问项目。
我正在尝试为我的项目创建一些单元测试,经过大量挖掘后我发现了 Effort,这个想法很棒,它模拟数据库而不是处理伪造 DBContext ,顺便说一句,这真的很难在使用复杂模式时正确处理。
但是,在我专门将用户的电子邮件添加到由 Effort 创建的内存数据库中后,我试图获取该用户的电子邮件,这是代码
MyContext contextx = new MyContext(Effort.DbConnectionFactory.CreateTransient());
var client = new Client
{
ClientId = 2,
PersonId = 3,
Person = new Person
{
PersonId = 3,
EMail = "xxxxx@gmail.com"
}
};
contextx.Client.Add(client); //<-- client got added, I checked it and is there
var email = contextx.Client.Select(c => c.Person.EMail).FirstOrDefault();
在上面的最后一行中,我无法发送到 return 电子邮件 xxxx@gmail.com,而是它总是 returns null。
有什么想法吗?
回答您的直接问题
对于您提出的具体问题,我有两点建议:
看看
contextx.Client.ToArray()
看看你在那个集合中真正有多少成员。可能Client
集合实际上是空的,在这种情况下你确实会得到 null。或者,可能是 Client 集合中的第一个元素具有EMail
. 的空值
如果在查询 DbContext 上的
Client
集合之前调用contextx.SaveChanges()
,行为会发生什么变化?我很好奇调用SaveChanges
是否会导致新插入的值存在于集合中。这确实不是必需的,但 Effort 和DbContext
. 之间可能存在一些奇怪的相互作用
编辑: SaveChanges()
结果就是答案。
一般测试建议
既然你用 "unit-testing" 标签标记了这个问题,我将根据我作为单元测试从业者和教练的十年经验提供一些一般的单元测试建议。 单元 测试是关于单独测试应用程序的各个小部分。通常这意味着单元测试一次只与几个 类 交互。这也意味着单元测试不应依赖于外部库或依赖项(例如数据库)。相反,集成 测试一次测试系统的更多部分,并且可能对数据库等事物具有外部依赖性。
虽然这看起来像是对术语的质疑,但这些术语对于将测试的实际意图传达给团队的其他成员很重要。
在这种情况下,要么您真的想对碰巧依赖于 DbContext 的某些功能进行单元测试,要么您正试图测试您的数据访问层。如果您尝试编写一个直接依赖于 DbContext 的独立单元测试,那么您需要打破对 DbContext 的依赖。我将在下面的 打破对 DbContext 的依赖性中对此进行解释。否则,您实际上是在尝试集成测试您的 DbContext,包括您的实体是如何映射的。在这种情况下,我总是发现最好隔离这些测试并使用真实的(本地)数据库。您可能希望使用本地安装的与您在生产中使用的数据库种类相同的数据库。通常,SqlExpress 工作得很好。将您的测试指向一个数据库实例,测试可以完全破坏该实例。让您的测试在每次测试 运行 之前删除任何现有数据。然后,他们可以设置他们需要的任何数据,而不用担心现有数据会发生冲突。
打破对 DbContext 的依赖
那么,当您的业务逻辑依赖于访问 DbContext
时,您如何编写好的单元测试? 你没有。
在我使用 Entity Framework 进行数据持久化的应用程序中,我确保对 DbContext
的访问包含在一个单独的数据访问项目中。通常,我会创建 类 来实现 Repository 模式,并且允许那些 类 依赖于 DbContext
。因此,在这种情况下,我将创建一个实现 IClientRepository
接口的 ClientRepository
。界面看起来像这样:
public interface IClientRepository {
Client GetClientByEMail(string email);
}
然后,任何需要访问该方法的 类 都可以使用基本存根/模拟/其他任何方式进行单元测试。不用担心 mocking DbContext
。您的数据访问层已包含在内,您可以使用真实数据库对其进行全面测试。有关如何测试数据访问层的一些建议,请参阅上文。
作为一个额外的好处,此接口的实现定义了在一个统一的地方通过电子邮件地址查找 Client
的含义。 IClientRepository
界面让你快速答题,"How do we query for Client
entities in our system?"
依赖于 DbContext
与允许域 类 依赖于连接字符串并且到处都有 ADO.Net 代码的测试问题的规模大致相同。这意味着您必须创建一个包含真实数据的真实数据存储(即使使用假数据库)。但是,如果您将对 DbContext
的访问包含在特定的数据访问程序集中,您会发现编写单元测试要容易得多。
就项目组织而言,我通常只允许我的数据访问项目引用 Entity Framework。我将有一个单独的核心项目,我在其中定义实体。我还将在 Core 项目中定义数据访问接口。然后,具体的接口实现被放入数据访问项目中。然后,您解决方案中的大多数项目都可以简单地依赖核心项目,只有顶级可执行文件或 Web 项目真正需要依赖数据访问项目。