公开方法来测试代码但不是最终用户?

时间:2016-10-31 04:03:03

标签: java design-patterns

假设:

public final class Dummy
{   
  /**
   * Constructor meant to be used for test code.
   * @param scope test-specific "globals"
   * @param value some user-supplied value
   */
  public Dummy(SingletonScope scope, int value)
  {
    // ...
  }

  /**
   * Constructor meant to be used by end-users.
   * @param value some user-supplied value
   */
  public Dummy(int value)
  {
    scope = someDefaultValue();
    // ...
  }
}

是否有一个设计模式允许我公开第一种方法来测试分散在多个包中的类,第二种方法是最终用户?

我不想将第一个构造函数暴露给最终用户,因为它混淆了API规范,我不希望它形成事实上的标准(类似于sun.misc.Unsafe)。解决方案不能使用依赖注入或反射。

5 个答案:

答案 0 :(得分:0)

  

解决方案不能使用依赖注入或反射。

如果您不能使用反射,则只能访问可访问的成员。

私有修饰符

您的测试类与生产类不同,也不是生产类的内部类。因此它无法访问私人会员。 (内部类只能访问外部类的私有成员,因为编译器使用package-private scope创建合成访问方法)

所以让我们尝试下一个修饰符。

<强>包私人

您可以将测试类放在与生产类相同的包中。然后,您可以访问包私有成员。但这也意味着同一个包中的所有其他生产代码都可以访问它们。

因此,包私有范围对你来说太过分了。

但是如果你不能使用反思,你就无法使私人成员可以访问。所以私人会员也不适合你。最后,java中没有任何访问修饰符。

答案 1 :(得分:0)

TL; DR

使用诸如PowerMock之类的模拟框架,模拟环境。

不要在生产代码中推送仅测试实现

它不仅会不必要地增加您要分发的二进制文件的大小,而且正如您所了解的那样,这很容易使该类型的界面变得混乱。

即使它不是您污染的公共界面,也可能会导致团队内部混淆,因此最好避免将测试代码与生产代码放在一起。测试代码应该只存在于它所属的位置:在测试中。

为什么你首先需要这样做?

您在评论中提到,这是您模拟软件环境的方式。模仿System.getProperty(String)SingletonScope的问题在于它们是静态方法。这意味着您无法覆盖此类方法来模拟其返回的值。

您的解决方案似乎是通过SingletonScope接口访问这些值,这是一个正确的想法(因为您可以简单地模拟SingletonScope)。问题是Map<String, String> mockedEnviornmentValues = ...; when(System.sysenv()).thenReturn(mockedEnviornmentValues); Properties mockedSystemProperties = ...; when(System.getProperties()).thenReturn(mockedSystemProperties); 仅用于测试目的:您只依赖于它,因此您可以模拟测试值。

解决方案

在开始测试之前,您需要模拟环境。使用PowerMock,您可以在测试期间将静态方法的返回值设置为特定值。

看起来像是:

{{1}}

答案 2 :(得分:-1)

使用final class和限制作为反射用法,我可以建议你 其他选择,但这些都是棘手的解决方案。 我没有看到任何可以解决您问题的简单设计模式。我们受Java语言规范的限制。

您可以依赖允许的package-private修饰符 在API中定义内部对象/处理并简化API的单元测试。

所以你可以:

  • 声明没有修饰符的方法(所以package-private)。 这样,如果他们使用不同的包,就不应该访问它。
  • 在同一个包中创建测试类,但是在测试文件夹中,以使包私有方法可见。

你可以拥有你的lib的智能客户端,它们可以通过使用相同的包级别来作弊(它应该是罕见的)。我认为在应用程序中你不应该关心它,因为它们是故意的棘手行为,而不是经典的API使用。

我看到另一种解决方案可以提供完美的API但更棘手:在编译类之前打包API时,运行一个删除代码源不良方法的任务。
如果它是单一的方法,它不应该是非常复杂的,但如果它是几种方法,它可能会变得很难做和维护。

答案 3 :(得分:-1)

您的问题标题提到了测试,但问题本身并没有。我会假设你是出于测试目的而这样做的。

一般来说,专门为测试编写生产代码是一个非常糟糕的主意。所有生产代码都应该有理由存在。交付到生产中的所有代码应该在生产中执行,否则它不应该存在。拥有它需要花费金钱和时间来维护并可能带来安全风险。

您应该开始对代码进行单元测试,就像您是最终用户一样,而不是了解被测试类的实现细节的特权用户 - 这通常被称为黑盒测试。这样做的目的是让您更轻松地更改被测设备的实施细节,而无需更改测试。通常这是通过首先编写测试来完成的。所有被测单位的合作者都应该被嘲笑或存根,你应该为此调查Mockito。

最后,你说解决方案不能使用DI或反射。这种约束很奇怪,违背了最佳实践。强烈建议您更改此内容。

答案 4 :(得分:-1)

他们说无法完成,但这是一个实际上对我有用的解决方案:

  • Dummy重命名为AbstractDummy
  • 将其辅助功能从public更改为package-protected
  • 在扩展Dummy的{​​{1}}和main代码库中创建名为test的新类。
  • 现在主要代码库中的妙语:AbstractDummy只公开最终用户应该看到的构造函数,而测试代码库中的Dummy只暴露自动化测试应该看到的构造函数。
  • Dummy代码库发送给最终用户。仅在内部使用main代码库(不要将其发送给最终用户)。

以下是最终产品的样子:

test

&#34; main&#34;基本代码:

abstract class AbstractDummy
{   
  /**
   * Constructor common to all implementations.
   * @param scope scope of "global" variables
   * @param value some user-supplied value
   */
  AbstractDummy(SingletonScope scope, int value)
  {
    // ...
  }
}

在&#34;测试&#34;基本代码:

public final class Dummy extends AbstractDummy
{   
  /**
   * Constructor meant to be used by end-users.
   * @param value some user-supplied value
   */
  public Dummy(int value)
  {
    super(MainSingletonScope.INSTANCE, value);
  }
}