繁体   English   中英

类中接口外的公共方法

[英]Public methods outside of an interface in a class

例:

public interface IFoo
{
    bool DoSomething();
}

public class Foo:IFoo
{
    public bool DoSomething()
    {
        var result = DoOtherThing();
        ...
        return result;
    }

    public bool DoOtherThing()
    {
        ...
    }
}

我的正常TDD方法是在DoSomething()DoOtherThing()方法上编写单元测试。 但是如果DoOtherThing是一个私有方法,这将很难做到。 我还读过,测试私有方法是禁止的。

是否可以在类上使用公共方法以便于代码覆盖和测试,即使只能通过其(IFoo)接口访问类的目的? 通常我会将超出接口范围的方法作为私有方法,但这不允许您有效地测试所有代码。 使方法公开允许您对Foo类进行正确的测试,但至少对我而言,拥有不从类外部调用的公共方法似乎是不正确的。 这种方式对TDD来说是最好的还是有更好的方法?

公共方法是公开的,因为它们应该是可访问的 如果不打算从外部调用它,请将其设为私有。

如果您未能获得代码覆盖率,您可能希望将您的类分解为多个类并使用组合代替。

难以测试的事情通常表明存在设计缺陷。

更新

好吧,让我们假设您有一种发送电子邮件的方法。 第1步是生成一个MailMessage类并填充它。 第2步是发送电子邮件

这是两个责任(SRP)。 撰写电子邮件并实际发送。 我不会在同一个班级那样做。 如果您的所有电子邮件类组成他们的邮件然后发送它们,它也将是代码重复。 你如何处理网络故障? 你也复制了那些支票吗?

做类似的事情:

public class SendWelcomeEmailComposer
{
    MailMessage Compose(User user)
}

public class EmailSender
{
    void SendEmail(MailMessage);
}

public class EmailService
{
    void SendWelcomeEmail(User user)
    {
        // compose email
        // and send using the classes above.
    }
}

更新2

为什么你不应该测试私有方法的原因是我关注质量测量。 如果你的测试覆盖率很低,你可能会违反一些基本原则(SOLID)。

因此,最好花时间反思课程设计而不是尝试测试私有方法。

这里的诀窍是使用

[assembly: InternalsVisibleTo("NameOfYourTestAssembly")] 

在AssemblyInfo.cs文件中。

这允许您将可测试方法设置为内部,这意味着它们只能在您正在编写的程序集中访问,以及此属性中的程序集。

(如果您有Mycode.dll和Mycode.Tests.dll,则将该属性添加到MyCode / Properties / AssemblyInfo.cs)

我认为你需要专注于测试什么 ,你会看到, 如何设计一个对象(或对象)通过测试的问题只是喜好和方便的问题。 你的问题没有一个正确的答案。

既然您决定分两步打破全局操作,那么您需要测试:

  1. 验证第一部分( DoSomething() )是否按预期运行。 这可能包括测试它是否调用正确的依赖项,将Foo对象置于正确的状态等。

  2. 验证第一步后面是第二步,换句话说DoSomething()调用DoOtherThing() ,如果需要,使用正确的参数。

  3. 验证第二步( DoOtherThing() )是否按预期运行。 同样,这可能包括它正确地与其依赖关系,产生正确的输出,等等。

没有到如何 虽然#1是非常简单的测试和实现,因为我们有DoSomething()公开的先决条件,#2和#3使您的实现和测试选项更加开放。 你基本上可以做两件事:

  • 将2个职责留在一个班级。 这个选项反过来会破坏许多可能性:使DoOtherThing() 公开 (易于测试但不安全,因为我们可能不希望将操作的内部子步骤暴露给外部),使其内部为@AlSki指出,使其受到虚拟保护,并在测试中使用部分模拟来验证两种方法之间的协作。 这个清单肯定还在继续。

  • 给每一步都有自己的类。 如果它们实际上是处理系统的不同部分或与不同层交谈的2个不同职责,那么这尤其重要。 您通常的模拟和协作测试适用于此处。

旁注1 :如果您未能区分操作中的2个职责并将这两个步骤保存在一个方法中,那么情况会有很大不同,因为您的测试实际上是集成测试而不是单元测试。 这可能会带来一些问题,例如只关注管道两端发生的事情,并且无法验证所有中间作业的正确性。 因此,我认为在尽可能多的合理步骤中分解大型操作总是更好(例如在电子邮件发送示例中,创建正确的MailMessage数据结构显然与发送它的责任不同)并测试每一个他们。

附注2 :您的类实现IFoo的事实与所有这些只是远程相关。 它基本上影响硬币的另一面 - 从其他类定义你的类的入口点。 如果要单独测试内容,则可能必须在IFoo使用者的测试中创建IFoo模拟,并验证这些使用者类是否正确调用了DoSomething()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM