Search code examples
javaunit-testingnetbeans-platform

How to test NetBeans Platform code which uses Lookups?


TL;DR How does one write unit tests for NetBeans Platform code which uses static methods to look up dependencies?

In a NetBeans platform application I come across code like this:

MyService service = Lookup.getDefault().lookup(MyService.class);
service.doStuff(....);

To me the static access seems like an antipattern and hard to test. When I Google around I only find comments about low coupling and high cohesion, teleinterfaces etc.

Many people seem to think this is a Good Idea but I am wondering how I can write a reasonable unit test for code like this, without resorting to mocking static methods or using the Lookup feature in my unit test.

The first idea that comes to my mind is to refactor the lookup as a regular dependency:

public class MyClass {

   private Lookup lookup = Lookup.getDefault();

   public void myMethod() {
       MyService service = lookup.lookup(MyService .class);
       service.doStuff(....);
   }

   public void setLookup(Lookup lookup) {
       this.lookup = lookup;
   }

And then use the setter to provide a mock Lookup for testing.

This would work, but still causes the tested code to call Lookup.getDefault() before setting the mock. There is no regular dependency injection mechanism provided by Netbeans Platform so if I introduce it like this it feels like swimming against the stream.

I get the feeling I am missing something. Is there a standard approach to write unit tests for Netbeans Platform code?


Solution

  • So far I found several ways of solving this.

    1 - Publish a test version of the class in Lookup with a higher position

    @org.openide.util.lookup.ServiceProvider(service = MyService.class, position = 1)
    public class TestService implements MyService {
        public void doStuff(....) {
    

    2 - Use NBJunit's MockService

    public class MyTest extends NbTestCase {
        public void setUp() throws Exception {     
            org.netbeans.junit.MockServices.setServices(TestService.class);
        }
    

    3- Register your own lookup implementation:

    static {
        System.setProperty("org.openide.util.Lookup", TestLookup.class.getName());
    }
    
    public class TestLookup extends org.openide.util.lookup.AbstractLookup {
        public TestLookup() {
            this(new org.openide.util.lookup.InstanceContent());
        }
    
        private TestLookup(org.openide.util.lookup.InstanceContent ic) { 
            super(ic);
            ic.add(new TestService());
        }
    

    Some of these ideas were found here: https://openide.netbeans.org/tutorial/test-patterns.html.