0

I am writing module tests for a project using testng and Mockito2. I want to mock a few methods which make outbound requests. Now, the object to mock is created locally within another object's method. So, if I have say, 4 classes, A, B, C and D, such that A creates an object of type B, B creates an object of type C and so on, and object of type D is to be mocked, I see I have two options to mock it.

Option 1 is to spy on objects of type A,B,C and inject spy of B into A and C into B and finally inject mock of of D into C during object creation. Following is an example.

class A {
        public B createB()
        {
            retrun new B();
        }
        public void someMethod ()
        {
            B b = createB();
        }
    }

In this way I can can spy on A and inject mock object for B when createB is called. This way I can ultimately mock D.

Option 2 is to not mock intermittent classes and directly have a Factory class like the one below:

class DFactory {
    private static D d;
    static public void setD (D newD)
    {
         d = newD;
    }
    public static D getD()
    {
        if (d!=null)
        {
            return d;
        } else
        {
            return new D();
        }
    }
}

The above option is simple, but I am not sure if this is the right thing to do as it creates more static methods, something that should be avoided, I believe.

I would like to know which method should be preferred and if there is some other alternative.

Please note that I do not wish to use powermockito or any other such frameworks which encourage bad code design. I want to stick to mockito2. I am fine with refactoring my code to make it more testable.

  • Recommend you read up on the concept of [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection). – Gene Jun 28 '18 at 19:48

1 Answers1

0

The way you have it now, with A creating B and B creating C and C creating D, all of that creation are implementation details you can't see or change, specifically the creation of dependency objects.

You are admirably avoiding the use of PowerMockito, and you are also admirably interested in refactoring your code to handle this change well, which means delegating the choice of D to the creator of A. Though I understand that you only really mean for this choice to happen in testing, the language doesn't know that; you are choosing a different implementation for the dependency, and taking the choice away from C's implementation. This is known as an inversion of control, or dependency injection. (You've probably heard of them before, but I introduce those terms at the end because they typically associated with weight and frameworks that aren't really necessary for this conversation right now.)

It's a little trickier because it looks like you don't just need an implementation of D, but that you need to create new implementations of D. That makes things a little harder, but not by much, especially if you are able to use Java 8 lambdas and method references. Anywhere below that you see a reference to D::new, that's a method reference to D's constructor that could be accepted as a Supplier<D> parameter.

I would restructure your class in one of the following ways:

  • Construct A like new A(), but leave the control over the implementation of D for when you actually call A, like aInstance.doSomething(new D()) or aInstance.doSomething(D::new). This means that C would delegate to the caller every single time you call a method, giving more control to the callers. Of course, you might choose to offer an overload of aInstance.doSomething() that internally calls aInstance.doSomething(new D()), to make the default case easy.
  • Construct A like new A(D::new), where A calls new B(dSupplier), and B calls new C(dSupplier). This makes it harder to substitute B and C in unit tests, but if the only likely change is to have the network stack represented by D, then you are only changing your code as required for your use-case.
  • Construct A like new A(new B(new C(D::new))). This means that A is only involved with its direct collaborator B, and makes it much easier to substitute any implementation of B into A's unit tests. This assumes that A only needs a single instance of B without needing to create it, which may not be a good assumption; if all classes need to create new instances of their children, A would accept a Supplier<B>, and A's construction would look like new A(() -> new B(() -> new C(D::new))). This is compact, but complicated, and you might choose to create an AFactory class that manages the creation of A and the configuration of its dependencies.

If the third option is tempting for you, and you think you might want to automatically generate a class like AFactory, consider looking into a dependency injection framework like Guice or Dagger.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251