使用C#和实体框架我一直在玩一些架构创意并碰壁。我的问题是如何对以下结构进行单元测试?但也许我应该在Software Engineering StackExchange上发帖并询问这是一种很好的架构方法吗?
理念是实体框架处理ID生成,并且可以创建具有这些ID集的类的实例,即使setter是私有的
public class MyClass
{
public int Id { get; private set; }
public int MyValue { get; set; }
}
这样,其他代码永远不会覆盖应该由数据库生成的Id值,必须将新数据库的新条目创建为新的实例化(而不是将Id更改为0或其他可疑行为)等。
但我现在在单元测试中使用这些类时遇到了麻烦。以前我会模拟数据库上下文,以便从上下文请求一个表只返回一个预期模型类的内存中集合,配置Id
等属性,以便其他操作如.Where(i => i.Id == reqestedId)
可以按预期工作:
mockContext.SetUp(c => c.MyClasses).Returns(myMockCollection);
如何使用这些模型来帮助测试应用程序的其他部分?为每个模型数据类创建接口似乎过多。指定一个internal
setter并使该类对测试项目可见是最好的方法吗?
答案 0 :(得分:1)
在EF Core 2.1中,实体类型可以有构造函数。
public class MyClass
{
public int Id { get; private set; }
public int MyValue { get; set; }
public MyClass(int id, int myValue)
{
this.Id = id;
this.MyValue = myValue;
}
}
EF Core将具有私有设置器的属性视为读写属性。 结果,映射将正常发生,即Id仍将成为主键,并且密钥也可以存储生成。 您可以阅读更多here
答案 1 :(得分:0)
似乎MyClass代表来自数据库的数据。通常,我将我的域类与DTO(数据传输对象)分开,而DTO具有所有公共getter和setter。我建议有一个单独的域对象和一个DTO。
例如,
CustomerRepository将知道如何从CustomerDto从数据库创建Customer对象,Customer.Id将被私下设置,但CustomerDto.Id将是公共的。
答案 2 :(得分:0)
尝试使用反射。通过反思,您也可以访问私人成员。
答案 3 :(得分:0)
我目前使用的方法是:
public class MyClass
{
public int Id { get; internal set; }
public int MyValue { get; set; }
}
在我的模型项目的AssemblyInfo.cs文件中使用[assembly:InternalsVisibleTo("my.Testing.Project")]
。
这样我的测试项目可以在需要时手动设置Id
,但其他代码则不能。它仍然暴露Id
在其定义的项目中滥用,但因为该项目主要是模型类,所有业务逻辑在一个单独的项目中相对安全。
答案 4 :(得分:0)
我认为没有必要隐藏实体ID字段并且颈部有这种痛苦。
首先,这种方法在某种意义上不是对称的,你可以没有代理Id(例如Guid Id等)。 (更正:Guid也是代理人。)
此外,有时您实际上需要能够设置标识字段,您可以通过为实体标识设置负int来在EntityFramework中执行此操作。在某些情况下,此选项非常有用。
实体框架不处理Id生成,我认为这是一个错误的声明,对于Identity Id键它只返回数据库生成的值。
恕我直言,这是一种糟糕的建筑方法。变体2.您可以使用显式界面。
public interface IEntity
{
int Id { get; set; }
}
public class UserEntity : IEntity
{
private int _id;
public int Id
{
get { return _id; }
}
int IEntity.Id
{
get { return _id;}
set { _id = value; }
}
public string Name { get; set; }
}
var user = new UserEntity();
((IEntity)user).Id = -1;
user.Name = "John";
var userId = user.Id;
还可以选择添加内部接口来设置Id值。