是否应该在接口中声明所有公共方法?

时间:2019-05-01 20:57:20

标签: c# unit-testing interface

给出如下界面:

public interface IFoo
{
    string Bar();
}

和一个实现它的类,如:

public class Foo : IFoo
{
    public string Bar()
    {
        returns "Bar";
    }

    public string SomeOtherMethod()
    {
        returns "Something else";
    }
}

此代码是否有问题?是否应该将所有成员添加到界面?

一个例子:想象一个私有方法,它变得足够复杂以至于需要进行单元测试。您可以将其公开(以便可以从测试项目中调用它),但是不能将其添加到接口中(因为没有客户端需要调用它)?

3 个答案:

答案 0 :(得分:2)

通常,当一个类实现一个接口时,必须实现该接口的所有成员,否则就不会相反。

此外,在实现接口成员时,实现类的相应成员也必须是公共的,非静态的,并且具有与接口成员相同的名称和参数签名。在接口中未定义的类中,甚至可以包含公共方法。

最重要的是,接口本身不提供类或结构可以继承基类功能的方式继承的功能。但是,如果基类实现了接口,则从基类派生的任何类都将继承该实现。

答案 1 :(得分:1)

如果一个类所做的事情很简单,我们可以同时测试其公共方法和私有方法,那么我们就可以测试公共方法。

如果私有方法变得如此复杂,以至于在公共方法和私有方法之间我们需要太多的测试组合,那么该将私有方法分为自己的类了。将私有方法公开将破坏该类的封装。即使我们不将方法添加到interface中,将类方法设置为公共方法仍会将方法添加到类本身的公共接口

所以,如果我们有这个:

public class ClassWithComplexPrivateMethod
{

    public void DoSomething(int value)
    {
        PrivateMethodThatNeedsItsOwnTests(value);
    }

    private string PrivateMethodThatNeedsItsOwnTests(int value)
    {
        // This method has gotten really complicated!
        return value.ToString();
    }
}

我们可能会重构为以下内容:

public interface IRepresentsWhatThePrivateMethodDid
{
    string MethodThatNeedsItsOwnTests(int value);
} 

public class RefactoredClass
{
    private readonly IRepresentsWhatThePrivateMethodDid _dependency;

    public RefactoredClass(IRepresentsWhatThePrivateMethodDid dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(int value)
    {
        return _dependency.MethodThatNeedsItsOwnTests(value);
    }
}

现在,一个新类实现了IRepresentsWhatThePrivateMethodDid

现在,当我们测试重构的类时,我们会模拟IRepresentsWhatThePrivateMethodDid,并为实现IRepresentsWhatThePrivateMethodDid的任何类编写单独的单元测试。

说将私有方法公开为公开会破坏封装似乎是矛盾的,但将其公开为自己的单独类却没有。有两个区别:

  • 重构的类不依赖于包含私有方法的新类。这取决于新界面。
  • 重构类和接口之间的交互仍然隐藏在其方法中。其他调用其公共方法的类不“知道”它如何使用其依赖项。 (实际上,其他类可能会依赖于抽象,而不是直接依赖于重构的类。)

当我们可以通过公共方法测试该类(包括其私有方法)时,也很容易对此感到厌烦并过早引入单独的依赖关系。我已经做了很多次,它会导致很多不必要的接口和额外的类。没有完美,只有我们尽最大的努力来平衡它。


再想一想:我们倾向于使用接口来表示依赖关系,但是我们不必这样做。如果我们提取的只是一个私有方法,那么也许可以用委托或Func来表示它,就像这样:

public class RefactoredClass
{
    private readonly Func<int, string> _dependency;

    public RefactoredClass(Func<int, string> dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(int value)
    {
        return _dependency(value);
    }
}

或者我们可以使用一个委托,我比Func更喜欢,因为它表明了函数的作用。

答案 2 :(得分:0)

接口应仅代表所需的API,而无需考虑单元测试和其他问题。


要测试类的 public 方法,您应该不执行 ,因为Test-project可以访问它们。您只需要实例化一个类并使用Mock注入所有必需的依赖项。

Example: testing class without any dependencies

Example: testing class with dependency


要测试类的私有方法,需要使它们对测试项目可见:

  • private 方法标记为internal
  • 使内部方法对测试项目可见:将指令 [assembly:InternalsVisibleTo(“ Project.Tests”)] 添加到 AssemblyInfo.cs 中(请参阅{{ 3}})