如何使用DUnit测试私有方法?

时间:2023-01-17 00:11:51

I have a class that I am unit testing into with DUnit. It has a number of methods some public methods and private methods.

我有一个班级,我正在与DUnit进行单元测试。它有一些方法,一些公共方法和私有方法。

type
  TAuth = class(TDataModule)
  private
    procedure PrivateMethod;
  public
    procedure PublicMethod;
  end;

In order to write a unit test for this class I have to make all the methods public.

为了为这个类写一个单元测试,我必须公开所有的方法。

Is there a different way to declare the private methods so that I can still test them but they are not public?

是否有一种不同的方式来声明私有方法,以便我仍然可以测试它们但它们不公开?

7 个解决方案

#1


You don't need to make them public. Protected will do. Then you can subtype the class for unit testing and surface the protected methods. Example:

你不需要公开它们。受保护的人会这样做。然后,您可以对类进行子类型以进行单元测试并显示受保护的方法。例:

type
  TAuth = class(TDataModule)
  protected
    procedure MethodIWantToUnitTest;
  public
    procedure PublicMethod;
  end;

Now you can subtype it for your unit test:

现在您可以为您的单元测试进行子类型化:

interface

uses
  TestFramework, Classes, AuthDM;

type
  // Test methods for class TAuthDM
  TestAuthDM = class(TTestCase)
     // stuff
  end;

  TAuthDMTester = class(TAuthDM)
  public
    procedure MethodIWantToUnitTestMadePublic;
  end;

implementation

procedure TAuthDMTester.MethodIWantToUnitTestMadePublic;
begin
  MethodIWantToUnitTest;
end;

However, if the methods you want to unit test are doing things so intimately with the data module that it is not safe to have them anything but private, then you should really consider refactoring the methods in order to segregate the code which needs to be unit tested and the code which accesses the innards of the data module.

但是,如果你想要进行单元测试的方法与数据模块密切相关,除了私有之外它们是不安全的,那么你应该考虑重构这些方法以便分离需要单元的代码。测试和访问数据模块内部的代码。

#2


It is a little hacky, but I like to use this conditional compilation directive:

这有点hacky,但我喜欢使用这个条件编译指令:

  {$IfNDef TEST}
  private
  {$EndIf}

Your unit test project should define TEST in project → conditional defines.

您的单元测试项目应该在项目中定义TEST→条件定义。

Without a visibility specification, they become published. Beware: if the private visibility isn't the first one in the class declaration, it will get the previous definition. A safer way, but more verbose and less clear, would be:

如果没有可见性规范,它们就会发布。注意:如果私有可见性不是类声明中的第一个,它将获得先前的定义。一种更安全的方式,但更详细,更不清楚,将是:

  private
  {$IfDef TEST}
  public
  {$EndIf}

This has some advantages over the subclassing or other approaches:

这比子类或其他方法有一些优点:

  • No extra complexity: no extra classes in your code
  • 没有额外的复杂性:代码中没有额外的类

  • Nobody can "mistakenly" subclass and override your class: you preserve your architecture
  • 没有人可以“错误地”子类化并覆盖您的类:保留您的体系结构

  • When you say a method is protected, you somewhat expect that it will be overridden. You are telling this for who is reading your code. A protected method that shouldn't be overridden will confuse your code readers, breaking my first programming principle: "code must be written to be read by other human beings."
  • 当你说一个方法受到保护时,你有点期望它会被覆盖。您正在告诉谁正在阅读您的代码。一个不应该被覆盖的受保护方法会让你的代码读者感到困惑,这违反了我的第一个编程原则:“代码必须写成其他人才能阅读”。

  • DUnit is in their own unit, not included everywhere
  • DUnit在他们自己的单位,不包括在任何地方

  • You don't touch messy RTTI.
  • 你不要触摸凌乱的RTTI。

I think it is a clearer solution, and better than the selected answer.

我认为这是一个更清晰的解决方案,并且比选定的答案更好。

When I use this, I also configure the test project to put the build objects in a different directory of the main project. This prevents the binaries with the TEST directive to mix with the other code.

当我使用它时,我还配置测试项目以将构建对象放在主项目的不同目录中。这可以防止带有TEST指令的二进制文件与其他代码混合。

#3


I recommend the "XUnit Test Patterns" book by Gerard Meszaros:

我推荐Gerard Meszaros的“XUnit Test Patterns”一书:

Test-Specific Subclass

Question: How can we make code testable when we need to access private state of the SUT?

问题:当我们需要访问SUT的私有状态时,我们如何才能使代码可测试?

Answer: Add methods that expose the state or behavior needed by the test to a subclass of the SUT.

答案:添加将测试所需的状态或行为暴露给SUT的子类的方法。

... If the system under test (SUT) was not designed specifically to be testable, we may find that the test cannot get access to state that it must initialize or verify at some point in the test.

...如果被测系统(SUT)不是专门设计为可测试的,我们可能会发现测试无法访问状态,表明它必须在测试的某个时刻进行初始化或验证。

The article also explains when to use it and which risks it carries.

该文章还解释了何时使用它以及它带来的风险。

#4


Put the DUnit code within your unit. You can then access anything you like.

将DUnit代码放在您的单元中。然后您可以访问任何您喜欢的内容。

#5


In general, when I get in this situation, I often realize that I'm violating the single responsibility principle. Of course I know nothing about your specific case, but MAYBE, that private methods should be in their own class. The TAuth would than have a reference to this new class in it's private section.

一般来说,当我遇到这种情况时,我常常意识到我违反了单一责任原则。当然,我对你的具体案例一无所知,但也许,私人方法应该属于他们自己的班级。 TAuth将在其私人部分中引用这个新类。

#6


With Extended RTTI (Delphi 2010 and newer), invoking private methods via RTTI is another option. This solution is also the top-rated answer in How do I test a class that has private methods, fields or inner classes?

使用扩展RTTI(Delphi 2010及更高版本),通过RTTI调用私有方法是另一种选择。此解决方案也是如何测试具有私有方法,字段或内部类的类的最佳答案?

#7


{$IFNDEF UNITEST}
private
{$ENDIF}

Simple solution, which hardly is a hack. I frequently need to test private methods and this technique adds as little complication as possible.

简单的解决方案,这几乎不是一个黑客。我经常需要测试私有方法,这种技术增加了尽可能少的复杂性。

#1


You don't need to make them public. Protected will do. Then you can subtype the class for unit testing and surface the protected methods. Example:

你不需要公开它们。受保护的人会这样做。然后,您可以对类进行子类型以进行单元测试并显示受保护的方法。例:

type
  TAuth = class(TDataModule)
  protected
    procedure MethodIWantToUnitTest;
  public
    procedure PublicMethod;
  end;

Now you can subtype it for your unit test:

现在您可以为您的单元测试进行子类型化:

interface

uses
  TestFramework, Classes, AuthDM;

type
  // Test methods for class TAuthDM
  TestAuthDM = class(TTestCase)
     // stuff
  end;

  TAuthDMTester = class(TAuthDM)
  public
    procedure MethodIWantToUnitTestMadePublic;
  end;

implementation

procedure TAuthDMTester.MethodIWantToUnitTestMadePublic;
begin
  MethodIWantToUnitTest;
end;

However, if the methods you want to unit test are doing things so intimately with the data module that it is not safe to have them anything but private, then you should really consider refactoring the methods in order to segregate the code which needs to be unit tested and the code which accesses the innards of the data module.

但是,如果你想要进行单元测试的方法与数据模块密切相关,除了私有之外它们是不安全的,那么你应该考虑重构这些方法以便分离需要单元的代码。测试和访问数据模块内部的代码。

#2


It is a little hacky, but I like to use this conditional compilation directive:

这有点hacky,但我喜欢使用这个条件编译指令:

  {$IfNDef TEST}
  private
  {$EndIf}

Your unit test project should define TEST in project → conditional defines.

您的单元测试项目应该在项目中定义TEST→条件定义。

Without a visibility specification, they become published. Beware: if the private visibility isn't the first one in the class declaration, it will get the previous definition. A safer way, but more verbose and less clear, would be:

如果没有可见性规范,它们就会发布。注意:如果私有可见性不是类声明中的第一个,它将获得先前的定义。一种更安全的方式,但更详细,更不清楚,将是:

  private
  {$IfDef TEST}
  public
  {$EndIf}

This has some advantages over the subclassing or other approaches:

这比子类或其他方法有一些优点:

  • No extra complexity: no extra classes in your code
  • 没有额外的复杂性:代码中没有额外的类

  • Nobody can "mistakenly" subclass and override your class: you preserve your architecture
  • 没有人可以“错误地”子类化并覆盖您的类:保留您的体系结构

  • When you say a method is protected, you somewhat expect that it will be overridden. You are telling this for who is reading your code. A protected method that shouldn't be overridden will confuse your code readers, breaking my first programming principle: "code must be written to be read by other human beings."
  • 当你说一个方法受到保护时,你有点期望它会被覆盖。您正在告诉谁正在阅读您的代码。一个不应该被覆盖的受保护方法会让你的代码读者感到困惑,这违反了我的第一个编程原则:“代码必须写成其他人才能阅读”。

  • DUnit is in their own unit, not included everywhere
  • DUnit在他们自己的单位,不包括在任何地方

  • You don't touch messy RTTI.
  • 你不要触摸凌乱的RTTI。

I think it is a clearer solution, and better than the selected answer.

我认为这是一个更清晰的解决方案,并且比选定的答案更好。

When I use this, I also configure the test project to put the build objects in a different directory of the main project. This prevents the binaries with the TEST directive to mix with the other code.

当我使用它时,我还配置测试项目以将构建对象放在主项目的不同目录中。这可以防止带有TEST指令的二进制文件与其他代码混合。

#3


I recommend the "XUnit Test Patterns" book by Gerard Meszaros:

我推荐Gerard Meszaros的“XUnit Test Patterns”一书:

Test-Specific Subclass

Question: How can we make code testable when we need to access private state of the SUT?

问题:当我们需要访问SUT的私有状态时,我们如何才能使代码可测试?

Answer: Add methods that expose the state or behavior needed by the test to a subclass of the SUT.

答案:添加将测试所需的状态或行为暴露给SUT的子类的方法。

... If the system under test (SUT) was not designed specifically to be testable, we may find that the test cannot get access to state that it must initialize or verify at some point in the test.

...如果被测系统(SUT)不是专门设计为可测试的,我们可能会发现测试无法访问状态,表明它必须在测试的某个时刻进行初始化或验证。

The article also explains when to use it and which risks it carries.

该文章还解释了何时使用它以及它带来的风险。

#4


Put the DUnit code within your unit. You can then access anything you like.

将DUnit代码放在您的单元中。然后您可以访问任何您喜欢的内容。

#5


In general, when I get in this situation, I often realize that I'm violating the single responsibility principle. Of course I know nothing about your specific case, but MAYBE, that private methods should be in their own class. The TAuth would than have a reference to this new class in it's private section.

一般来说,当我遇到这种情况时,我常常意识到我违反了单一责任原则。当然,我对你的具体案例一无所知,但也许,私人方法应该属于他们自己的班级。 TAuth将在其私人部分中引用这个新类。

#6


With Extended RTTI (Delphi 2010 and newer), invoking private methods via RTTI is another option. This solution is also the top-rated answer in How do I test a class that has private methods, fields or inner classes?

使用扩展RTTI(Delphi 2010及更高版本),通过RTTI调用私有方法是另一种选择。此解决方案也是如何测试具有私有方法,字段或内部类的类的最佳答案?

#7


{$IFNDEF UNITEST}
private
{$ENDIF}

Simple solution, which hardly is a hack. I frequently need to test private methods and this technique adds as little complication as possible.

简单的解决方案,这几乎不是一个黑客。我经常需要测试私有方法,这种技术增加了尽可能少的复杂性。