softwareRat
softwareRat

Reputation: 55

Java - Inject bean only in super constructor without field @Autowired

I am dealing with a design situation and I am not sure how to solve it or if I am doing anything wrong with my micro design.

public abstract class A implements SomeFrameworkInterface {

  private final Param1 a1;

  @Inject
  private Param2 a2;

  protected A(Param1 a1) {
    this.a1 = a1;
  }

  @Override
  public someFrameworkInterfaceMethod() {
    //code
    doMyStuff();
  }

  protected abstract void doMyStuff();
} 

@Named
@SomeCustomAnnotation(property="someProperty")
public class B extends A {

  @Inject
  public B(Param1 b1) { //b1 is different for every implementation of class A, through some annotations
    super(b1);
  }

  @Override
  protected void doMyStuff() {
    //code
  }

}

Restrictions are the following:

  1. 3rd party framework with custom annotations to be used and interface to be implemented
  2. code must be testable, so @Inject/@Autowired on fields is not allowed
  3. we are restricted from using Spring which is "encapsulated" in the custom framework mentioned, so we can use only pure Java (JSR-330). (if any solution in Spring, would be nice to know about it too)

Upvotes: 1

Views: 545

Answers (2)

softwareRat
softwareRat

Reputation: 55

I find the solution to test this, but it's look a little weird. I mean I do not understand why or how is this happening, but it does the job in a clean way. Test class looks like:

@RunWith(MockitoJUnitRunner.class)
public BTest {

  @Mock
  private Param1 mockParam1;

  @Mock
  private Param2 mockParam2;

  @InjectedMocks
  private A b = new B(null);

  @Test
  public someFrameworkInterfaceMethodTest() {
    when(...).thenReturn(...)
    b.someFrameworkInterfaceMethod();
    verify(...).someCall(any());
  }

}

I had a hard time to figure it out why Param1 a1 remained null and tried to find out different solutions. But it seems Mockito injects fields again post construct (after "new B(null)" is called - if fields are null or not sure). Param2 a2 was mocked by mockito while Param1 a1 remained null and this happened because of the "final" keyword on Param1 a1 in B class.

Upvotes: 0

jingx
jingx

Reputation: 4014

It's impossible to pass any parameter to A's constructor without going through B's, because you are actually constructing an instance of B. A's constructor is only delegated to in order to construct the "A part" of the B instance.

The most you can do is pass some sort of "typeless" parameter to B's constructor, so that at least B won't know about Param2, but that cure already sounds worse than the disease even as I was writing it down...

I don't see injecting Param2 post-construction making the code any less testable. OTOH, using a static field would most certainly do.

Upvotes: 0

Related Questions