adam bien's blog

@Inject With Package-Private Fields Only, Or Harmful Accessors 📎

I do not use private, or any other modifier for all injected (@Inject, @EJB, @PersistenceContext) fields:

@Startup
@Singleton
//...
public class Hits {

    @Inject
    XRayLogger LOG;
    @EJB
    PersistentHitStore hitStore;
    @EJB
    PersistentRefererStore refererStore;
}

This simplifies mocking considerably: your unit tests (hopefully) reside in the same package as the production and can directly access all the injected fields:

class HitsTest extends JUnitSuite with MockitoSugar with ShouldMatchersForJUnit {

  var cut: Hits = _

  @Before
  def setUp: Unit = {
    cut = new Hits()
    cut.LOG = new DevNullLogger()
    cut.hitStore = mock[PersistentHitStore];
    cut.monitoring = mock[Event[Diagnostics]];
    cut.refererStore = mock[PersistentRefererStore]
}

[from x-ray]
Package-private fields do not belong to the EJB 3.1 no-interface view and are not visible to other bean instances.

An alternative approach would be to make a field private and then expose it via a public setter:


private XRayLogger LOG;

@Inject
public void setLog(XrayLogger log){
	this.LOG = log;
}

Although the example above implies encapsulation, it actually breaks it. A public method belongs per convention to a non-interface view of an EJB 3.1 and could be regularly invoked from the injected instance. You could of course add an explicit interface to hide the setter, but this would be the first step towards overengineering.

[All x-ray unit tests are accessing the package-private fields directly. See aslo page 110 "Injection and Infrastructure Testing with Aliens" in Real World Java EE Night Hacks--Dissecting the Business Tier.]