在编写测试时使用私有setter for Ids

时间:2018-04-05 14:41:08

标签: c# entity-framework architecture software-design

使用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并使该类对测试项目可见是最好的方法吗?

5 个答案:

答案 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。

例如,

  • 客户:域类
  • CustomerDto:一个保存数据库数据的属性包

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值。