【问题标题】:testing private methods in the test classes [duplicate]在测试类中测试私有方法[重复]
【发布时间】:2019-01-23 14:56:31
【问题描述】:

我正在测试一个类,该类有一个私有方法,并在内部从公共方法调用。我希望能够伪造这个测试方法,以便永远不会调用实际方法。

public class Service
{
    public int MethodA(int a)
    {
        SaveToDB(a);
        if (validate())
        {
                return a * 5;
        }
        else 
        {
            return 0;
        }
    }

  private bool validate(int a)
  {
        if (a > 100)
          return true;
        else 
          return false; 
   }

    private bool SaveToDB()
    {
     // some logic to save to the database..
     return true;
    }
 }

[FixtureTest]
public ServiceTest
{
//assuming we are using nunit and fakeiteasy..
[Test]
public void MethodA_Should_Return_Zero_when_Provided_100()
{
    var fakeService = new Service;
    var result = fakeservice.MethodA(101);
    // I want to avoid the call SaveToDB() in the test how do I go about doing this..

 //if this was a public method I could create a test stub and test like with a statement like 
 A.call(() => ServiceA.SaveToDB().Return())
// however this is a private function what should I do???
}
}

【问题讨论】:

  • 这里有2个问题。 1)你不能访问类范围之外的私有方法。所以没有办法测试/模拟私有方法。 2)您不能模拟 db 调用,因为它是服务层的一部分。如果您将服务层和 DAL 层分开,这将更容易模拟。
  • 我的第一个想法是依赖注入。如果服务会被注入一些 IDbSaver,它执行保存到数据库,你的测试很容易注入一个假的。
  • 你可以反过来做。测试私有函数(通过反射)以证明它们有效,然后测试公共函数。不好用,用 DI 会更干净更好,但有时脏是唯一的办法(只要确保在关闭计算机时擦手即可)。

标签: c# unit-testing nunit fakeiteasy


【解决方案1】:

最简单的答案是使SaveToDB protected virtual 并在测试项目中创建一个可测试的派生类。

public class FakeTestService : Service
{
    ...

    protected override bool SaveToDB()
    {
        // do nothing

        return true;
    }
}

在你的测试中使用FakeTestService,“真实的”MethodA 将执行,但SaveToDB 不会执行任何操作。不过,真正的答案是使用某种形式的控制反转向您的数据库注入一个接口,该接口可以通过模拟库进行模拟。

public class Service
{
    public int MethodA(int a)
    {
        DatabaseThinger.SaveToDB(a);
        if (validate())
        {
            return a * 5;
        }
        else 
        {
            return 0;
        }
    }

    private IDatabaseThinger DatabaseThinger;

    public Service(IDatabaseThinger databaseThinger)
    {
        DatabaseThinger = databaseThinger;
    }
}

因此,将数据库功能从类中移出,并将其放在接口后面。然后,您可以在测试时注入您选择的 IDatabaseThinger 的实现,但实际上不会连接到数据库。

【讨论】:

  • 如果我引入一个接口,然后我可以为接口对象及其方法创建假的?
  • 如果我引入接口,那么这个调用会不会是公开的,那样我们正在改变封装?我被告知不要更改服务方法的可访问性。
  • 添加了更多细节。
猜你喜欢
  • 1970-01-01
  • 2019-07-13
  • 1970-01-01
  • 2015-03-14
  • 2018-01-15
  • 2019-02-01
  • 2020-11-08
  • 2019-08-04
  • 1970-01-01
相关资源
最近更新 更多