1

I'm attempting to design a class that is to be instantiated through the use of a static method, something like newInstance(param1, param2). The reason behind this is that 2 of the 4 parameters it takes are from the same package, but I want to hide them from the public.

In order to achieve this, I made the constructor private. However, I just realize that I am now unable to pass the 2 parameters on my tests.

It goes like this

class Foo {
    private constructor(p1, p2, p3, p4) {}

    public static newInstance(p1, p2) {
        p3 = new p3;
        p4 = new p4;
        return new static(p1, p2, p3, p4);
    }
}

Obviously my design is wrong, but I can't think of a way to abstract the existence of p3 and p4 to other developers, while still being able to pass them as dependency in order to apply dependency inversion.

What design can I use to achieve this? Maybe I should just create a FooFactory and document that they're supposed to use that...


I'm basically writing an SDK for an REST API. These REST API is not designed by me nor my company (so I can't make the REST API follow industry standards), and it has a very complicated authentication mechanism. It is complicated enough that I want to achieve the following:

  1. Break it down into little testable components, so that it's easier to maintain, and the other developers are able to work with just the endpoints the SDK expose, which encapsulate the domain model.
  2. Abstract the construction of the dependency graph for the SDK, so that they can just use the CompanyClient class and call business domain methods like getCustomerList.
  3. Completely abstract the construction of the customized headers, parameters, etc... of the HTTP request.

I agree this work as a unit so it make sense just to test the CompanyAPI class by itself; However, that would make it difficult to maintain the sub-mechanisms that are needed to make it work.

In summary, by breaking it down into smaller pieces, it's easy to test that every mechanism works according to the specification.

For this reason I want just to expose 2 parameters for the library users. They don't need to know the underlying implementation. However, I DO need to test and since the dependencies include a HTTP client, I need to inject mock objects.

  • 1
    In what language? In C#, you can use internal tricks or reflection to instantiate the class for tests. – Telastyn Aug 28 '17 at 16:40
  • Why can't you pass the 2 parameters on your tests, exactly? – JimmyJames Aug 28 '17 at 16:48
  • 1
    @JimmyJames: Have you looked at the code? – Robert Harvey Aug 28 '17 at 16:49
  • "The reason behind this is that 2 of the 4 parameters it takes are from the same package, but I want to hide them from the public." - Why do you want to hide these Parameters? And if you cannot change the parameter values (for the final program) why should they be parameters at all? – Timothy Truckle Aug 28 '17 at 16:56
  • 1
    Not sure why someone down-voted this question. The question is very clear: How do I make a class testable when the constructor is private and new instances are created from a static method? – Greg Burghardt Aug 28 '17 at 17:28
4

If you are using c#, you can use this "friend" approach:

Add another (possibly parameterless) constructor to the class solely for your test project.

"Hide" the constructor from other developers by making it internal. You can then make the constructor visible to your test project by using the InternalsVisibleToAttribute.

[assembly:InternalsVisibleTo("MyUnitTestProject")]

class Foo {
    internal constructor(p1, p2, p3, p4) {}

    public static newInstance(p1, p2) {
        p3 = new p3;
        p4 = new p4;
        return new static(p1, p2, p3, p4);
    }
}

If you are not using c#, or would rather not use "friend" attributes:

  1. Create a non-static factory for this class, along with an interface for the factory, e.g. IFooFactory.

    interface IFooFactory { Foo newInstance(p1, p2); }
    
    class FooFactory : IFooFactory
    {
        public Foo newInstance(p1, p2)
        {
            return new Foo(p1, p2, new p3(), new p4());
        }
    }
    
  2. In production code, inject the non-static factory into any class that needs to be able to instantiate a Foo. Use your injection container to ensure that there is only one instance of the factory per thread/process/whatever is appropriate for your application.

    class SomeOperation
    {
        private readonly IFooFactory _fooFactory;
    
        public SomeOperation(IFooFactory fooFactory)
        {
            _foofactory = fooFactory;
        }
    }
    
    
    class CompositionRoot
    {
        public RegisterDependencies(IContainer container)
        {
            container.RegisterType<IFooFactory, FooFactory>().instancePerProcess();  //Syntax may vary
            container.RegisterType<SomeOperation, SomeOperation>();
        }
    }
    
  3. Replace any call to

    Foo.newInstance(p1, p2)
    

    with

    _fooFactory.newInstance(p1, p2);
    
  4. In test code, write your own test factory that conforms to IFooFactory and inject it instead of the production factory. Your test factory may be able to get away with supplying dummy p3 and p4 or you can stub those too.

  • As a summary, go for the factory pattern approach, right? – Christopher Francisco Aug 28 '17 at 19:02
  • Exactly. You can stub a factory, but it's pretty hard to stub a static method. – John Wu Aug 28 '17 at 19:20
3

Based on what you want, it feels Foo, along with p3 and p4 all form a unit. As such, they should be tested together as a unit. Which means instantiating Foo through the static method is perfectly fine for unit test.

If this results in too complicated tests, then that means your decision to not be able to instantiate Foo with different classes might not be correct one.

  • 1
    I agree, this static method is 100% equivalent to a constructor that resolves the p3, p4 dependencies itself. It is neither inherently testable or untestable. – amon Aug 28 '17 at 17:14
  • As such, they should be tested together as a unit., I thought about this approach too, but p4 is basically an HTTP client so it be better just to inject a mock on the tests. – Christopher Francisco Aug 28 '17 at 19:01
  • Read my edit for more context on the problem. It is possible that I'm just going with the incorrect solution for this problem – Christopher Francisco Aug 28 '17 at 19:14
  • @ChristopherFrancisco So what is your reason to force creation of Foo through static method? I believe that if there is complex construction going on, then it should be exposed to the user. – Euphoric Aug 28 '17 at 19:15
  • 1
    @ChristopherFrancisco If that is a problem, then solution should be done by splitting places where class is created and where it is used. If code needs to use Foo, it shouldn't create it itself. It should get it already created. Maybe some kind of Factory class should be used instead of static method. – Euphoric Aug 28 '17 at 19:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.