我正在尝试为我的项目创建一些单元测试,经过多次挖掘后我发现了Effort,这个想法很棒,它嘲笑数据库而不是伪造DBContext,顺便说一句很难得到它在使用复杂模式时是正确的。
然而,在我专门将其添加到由Effort创建的内存数据库中之后,我正试图获取用户的电子邮件,这里是代码
class Hero(object):
def __init__(self, name, lvl, _str, agi, vit, _int, luk):
self.name = name
self.lvl = lvl
self._str = _str # Should not use "str" because of reserved keyword of the same name
self.agi = agi
self.vit = vit
self._int = _int # Should not use "int" because of reserved keyword of the same name
self.luk = luk
self.exp = 0
@property
def hp(self):
return 19 * self.vit
class HeroProf_1(Hero):
skillList = ['heavySlash01']
strUp = 3
agiUp = 1
vitUp = 2
intUp = 1
lukUp = 1
@property
def dmg(self):
return 3 * self._str + 1 * self.agi
class HeroProf_2(Hero):
skillList = ['doubleAttack02']
strUp = 1
agiUp = 3
vitUp = 1
intUp = 1
lukUp = 2
@property
def dmg(self):
return 1 * self._str + 3 * self.agi
class HeroProf_3(Hero):
skillList = ['fireBall03']
strUp = 1
agiUp = 1.5
vitUp = 0.5
intUp = 3.5
lukUp = 1.5
@property
def dmg(self):
return 4 * self._int
在上面的最后一行我不能让它返回电子邮件xxxx@gmail.com而是总是返回null。
有什么想法吗?
答案 0 :(得分:1)
对于您提出的具体问题,我建议两件事:
看一下contextx.Client.ToArray()
,看看你在该系列中真正拥有多少成员。可能是Client
集合实际上是空的,在这种情况下你确实会得到null。或者,可能是Client集合中的第一个元素具有EMail
的空值。
如果在查询DbContext上的contextx.SaveChanges()
集合之前调用Client
,行为会如何变化?我很想知道调用SaveChanges
是否会导致新插入的值存在于集合中。这确实不应该是必需的,但是在Effort和DbContext
之间可能会有一些奇怪的互动。
编辑: SaveChanges()
原来是答案。
由于您使用“单元测试”标签标记了这个问题,我将根据我作为单元测试从业者和教练花费的十年时间提供一些一般的单元测试建议。 单元测试是关于单独测试应用程序的各个小部分。通常,这意味着单元测试只能同时与几个类进行交互。这也意味着单元测试不应该依赖于外部库或依赖项(例如数据库)。相反,集成测试会同时执行系统的更多部分,并且可能对数据库等内容具有外部依赖性。
虽然这看起来似乎是对术语的狡辩,但这些术语对于将测试的实际意图传达给团队中的其他成员非常重要。
在这种情况下,要么您真的想要对依赖于DbContext的某些功能进行单元测试,要么尝试测试您的数据访问层。如果您正在尝试直接编写依赖于DbContext的单元测试,那么您需要打破对DbContext的依赖。我将在下面的打破对DbContext的依赖中解释这一点。否则,您真的尝试集成测试您的DbContext,包括您的实体如何映射。在这种情况下,我总是发现最好隔离这些测试并使用真实(本地)数据库。您可能希望使用与您在生产中使用的相同种类的本地安装的数据库。通常,SqlExpress工作得很好。将测试指向测试可以完全丢弃的数据库实例。在运行每个测试之前,让测试删除所有现有数据。然后,他们可以设置他们需要的任何数据,而无需担心现有数据会发生冲突。
那么,当您的业务逻辑依赖于访问DbContext
时,您如何编写好的单元测试? 你没有。
在我使用Entity Framework进行数据持久化的应用程序中,我确保DbContext
的访问权限包含在单独的数据访问项目中。通常,我将创建实现Repository模式的类,并允许这些类依赖DbContext
。所以,在这种情况下,我会创建一个实现ClientRepository
接口的IClientRepository
。界面看起来像这样:
public interface IClientRepository {
Client GetClientByEMail(string email);
}
然后,任何需要访问该方法的类都可以使用基本的stub / mock / whatever进行单元测试。没有什么可以担心嘲笑DbContext
。您的数据访问层已包含,您可以使用真实数据库对其进行彻底测试。有关如何测试数据访问层的一些建议,请参见上文。
作为一个额外的好处,此接口的实现定义了在单个统一的位置通过电子邮件地址查找Client
的含义。 IClientRepository
界面可让您快速回答问题“我们如何查询系统中的Client
个实体?”
依赖DbContext
与测试问题的大小基本相同,因为允许域类依赖于连接字符串并在任何地方都使用ADO.Net代码。这意味着您必须创建一个包含真实数据的真实数据存储(即使使用虚假数据库)。但是,如果您包含对特定数据访问程序集中DbContext
的访问权限,您会发现单元测试更容易编写。
就项目组织而言,我通常只允许我的数据访问项目参考实体框架。我将有一个单独的Core项目,我在其中定义实体。我还将在Core项目中定义数据访问接口。然后,具体的接口实现被放入数据访问项目中。您的解决方案中的大多数项目可以简单地依赖于Core项目,只有顶级可执行文件或Web项目才真正需要依赖于数据访问项目。