Gili Gili - 19 days ago 5
Java Question

Expose methods to test code but not to end-users?

Given:

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();
// ...
}
}


Is there a design pattern that will allow me to expose the first method to test classes that are scattered across multiple packages, and the second method to end-users?

I don't want to expose the first constructor to end-users because it clutters the API specification and I don't want it to form a de-facto standard (similar to
sun.misc.Unsafe
). The solution cannot use dependency injection or reflection.

Answer

They said it couldn't be done, but here is a solution that actually worked for me:

  • Rename Dummy to AbstractDummy.
  • Change its accessibility from public to package-protected.
  • Create new classes called Dummy in both the main and test codebases that extends AbstractDummy.
  • Now for the punchline: Dummy in the main codebase only exposes constructors that the end-user should see while Dummy in the test codebase only exposes constructors that the automated tests should see.
  • Ship the main codebase to end-users. Only use the test codebase internally (do not ship it to end-users).

Here is what the final product looks like:

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)
  {
    // ...
  }
}

In "main" codebase:

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);
  }
}

In "test" codebase:

public final class Dummy extends AbstractDummy
{   
 /**
   * 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)
  {
    super(scope);
    this.value = value;
  }
}