当伪造数据库时,FirstOrDefault返回null
本文关键字:FirstOrDefault 返回 null 伪造 数据库 | 更新日期: 2023-09-27 18:02:23
我试图为我的项目创建一些单元测试,经过大量挖掘,我发现努力,这个想法是伟大的,它模拟数据库而不是处理伪造DBContext,顺便说一下,在使用复杂的模式时很难得到正确的。
然而,我试图得到一个用户的电子邮件后,我特别将其添加到由努力创建的内存数据库,这里是代码
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();
在上面的最后一行中,我不能使它返回电子邮件xxxx@gmail.com,而是它总是返回null。
任何想法?
直接回答问题
对于你提出的具体问题,我有两点建议:
-
查看
contextx.Client.ToArray()
,看看该集合中真正有多少成员。可能Client
集合实际上是空的,在这种情况下,您确实会得到null。或者,它可能是Client集合中的第一个元素对于EMail
具有空值。 -
如果在DbContext上查询
Client
集合之前调用contextx.SaveChanges()
,行为会如何变化?我很想知道调用SaveChanges
是否会导致新插入的值存在于集合中。这真的不应该是必需的,但是在Effort和DbContext
之间可能会有一些奇怪的相互作用。
EDIT: SaveChanges()
原来是答案。
一般测试建议
既然你给这个问题加上了"单元测试"标签,我将根据我作为单元测试实践者和教练的十年经验,提供一些一般的单元测试建议。单元测试是关于隔离地测试应用程序的各个小部分。这通常意味着单元测试一次只与几个类交互。这也意味着单元测试不应该依赖于外部库或依赖项(比如数据库)。相反,一个集成测试同时测试系统的更多部分,并且可能有外部依赖,比如数据库。
虽然这看起来像是对术语的吹毛求疵,但术语对于向团队的其他成员传达测试的实际意图是很重要的。
在这种情况下,要么你真的想要单元测试一些碰巧依赖于DbContext的功能,要么你正在尝试测试你的数据访问层。如果您试图为直接依赖于DbContext的东西编写一个独立的单元测试,那么您需要打破对DbContext的依赖。我将在下面的中打破对DbContext的依赖来解释这一点。否则,您实际上是在尝试集成测试DbContext,包括实体是如何映射的。在这种情况下,我总是发现最好隔离这些测试并使用真正的(本地)数据库。您可能希望使用与生产中使用的数据库类型相同的本地安装数据库。通常,SqlExpress工作得很好。将测试指向测试可以完全废弃的数据库实例。让您的测试在运行每个测试之前删除所有现有数据。然后,他们可以设置他们需要的任何数据,而不用担心现有的数据会冲突。
打破对DbContext的依赖
那么,当您的业务逻辑依赖于访问DbContext
时,您如何编写好的单元测试?你不。
在我使用实体框架进行数据持久化的应用程序中,我确保对DbContext
的访问包含在单独的数据访问项目中。通常,我将创建实现Repository模式的类,并且允许这些类依赖于DbContext
。因此,在本例中,我将创建实现IClientRepository
接口的ClientRepository
。接口看起来像这样:
public interface IClientRepository {
Client GetClientByEMail(string email);
}
然后,任何需要访问该方法的类都可以使用基本的存根/mock/任何东西进行单元测试。不用担心模仿DbContext
。包含了数据访问层,您可以使用真实的数据库对其进行彻底测试。有关如何测试数据访问层的一些建议,请参阅上文。
作为一个额外的好处,这个接口的实现定义了在一个单一的、统一的地方通过电子邮件地址查找Client
的含义。IClientRepository
接口允许您快速回答问题,"我们如何查询系统中的Client
实体?"
对DbContext
的依赖与允许域类对连接字符串的依赖和ADO的测试问题的规模大致相同。Net代码无处不在。这意味着您必须创建一个包含真实数据的真实数据存储(甚至使用假数据库)。但是,如果您将对DbContext
的访问包含在特定的数据访问程序集中,您会发现编写单元测试要容易得多。
就项目组织而言,我通常只允许我的数据访问项目引用实体框架。我将有一个单独的Core项目,我在其中定义实体。我还将在Core项目中定义数据访问接口。然后,将具体的接口实现放到数据访问项目中。在你的解决方案中,大多数项目都可以简单地依赖于核心项目,只有顶级的可执行或web项目真正需要依赖于数据访问项目。