C# - 单元测试视图模型的 public getter 而不调用 class 构造函数?

C# - Unit test view model's public getter without calling class constructor?

如何在不调用 class 的构造函数的情况下从单元测试调用 C# 属性 的 public getter?

我目前正在为视图模型编写单元测试。

我没有测试私有方法,只测试 public 方法。
我正在测试任何实现自定义逻辑或验证的 public getters 和 setter。

我的 class 包含一个私人项目列表。
我的 class 包含一个 属性,其中 returns 其中一些项目基于项目的 属性 值。
我的 class 包含一个调用数据库的构造函数。

我想测试 getter returns 那些项目是否正确,因为 getter 是 class 的 public 界面的一部分,并且应该测试一下。

我的问题是我想以某种方式模拟我的 class,以便我可以在单元测试期间调用 class 属性 的 getter。但是,我不想调用 class 的构造函数,因为这意味着调用数据库并执行测试预期范围之外的其他工作。


这是一个猴子和香蕉的例子:

public class Monkey {

    private List<Banana> bananas;

    public Monkey()
    {
        this.GetBananasFromDatabase()
    }

    public List<Banana> PeeledBananas {
        get {
            List<Banana> peeledBananas = new List<Banana>();
            foreach(Banana banana in this.bananas)
            {
                if(banana.IsPeeled)
                {
                    peeledBananas.Add(banana);
                }
            }
            return peeledBananas;
        }
    }

    private GetBananasFromDatabase()
    {
        this.bananas = Database.GetBananas();
    }
}

在这个例子中,我想为 PeeledBananas 属性 测试 getter。
我不想调用构造函数,因为我不想调用 GetBananasFromDatabase() 方法。

PeeledBananas getter 是 monkey class 的 public API 的一部分,因此应该对其进行单元测试。但是测试的时候应该不会调用数据库。

我可以创建一个新的 monkey 模拟,无论如何我都可以调用构造函数并模拟 GetBananasFromDatabase() 方法的行为,但我不能模拟 GetBananasFromDatabase() 方法因为它是私人的。

解决这个问题的好方法是什么?

Nkosi 的评论提供了有用的见解。我的问题是我的代码由于使用了具体的数据库类型而紧密耦合。通过实践多态性和控制反转 (IOC),我决定将数据库接口传递给我的构造函数并调用方法,而不是调用具体数据库的方法。

现在,当我想对某些东西进行单元测试时,我只需将一个伪造的数据库对象传递到 monkey 构造函数中,而不必触及 GetBananasFromDatabase() 方法。

现在的代码如下所示:

public class Monkey {

    private List<Banana> bananas;
    private IDatabase database;

    public Monkey(IDatabase database)
    {
        this.database = database;
        this.GetBananasFromDatabase()
    }

    public List<Banana> PeeledBananas {
        get {
            List<Banana> peeledBananas = new List<Banana>();
            foreach(Banana banana in this.bananas)
            {
                if(banana.IsPeeled)
                {
                    peeledBananas.Add(banana);
                }
            }
            return peeledBananas;
        }
    }

    private GetInfoFromDatabase()
    {
        this.bananas = this.database.GetBananas();
    }
}

现在我的 monkey 可以从任何 database 对给定上下文有意义的东西中得到 bananas,并且 monkey class 不再紧密耦合特定的 IDatabase 实现。

当我想在生产代码中使用 monkey class 时,我用一些 RealDatabase 实例化了 class。当我想对 monkey class 进行单元测试时,我用一些 FakeDatabase 实例化了 class。两者都实现了 IDatabaseGetBananas(),因此 monkey 无论如何都有效。

谢谢你的提示,Nkosi!