0

I wonder if it is possible to test somehow class which is dependent on static final field? For example I have the following class:

public final class Foo
{
    private static Foo instance;
    public static final String BAR_FILE = "/usr/bin/bar.bar";

    private Foo()
    {
        loadBar();
    }

    public static synchronized Foo getInstance()
    {
        if (instance == null)
        {
            instance = new Foo();
        }
        return instance;
    }

    private void loadBar()
    {
        final Properties prop = new Properties();
        try
        {
            final FileInputStream fis = new FileInputStream(BAR_FILE);
            prop.load(fis);
            fis.close();
        }
        catch (final IOException ex)
        {
            System.out.println("Exception!");
        }
    }
}

So how can I test getInstance() (I know there is nothing to test, but this is just an example) method on windows if BAR_FILE is hardcoded and equals to some unix-style path. I tried to change this field via reflection, but no luck, and, moreover, in accordance with this discussion it is not even possible. Any help is appreciated!

Community
  • 1
  • 1
mr.nothing
  • 5,141
  • 10
  • 53
  • 77
  • Before your test method, just create a file named after the static field. – Sotirios Delimanolis Jul 03 '13 at 14:31
  • 3
    Fundamentally, this class isn't designed in a test-friendly way. If you can change it to avoid it being a singleton, that would really help... you could easily put a factory in to avoid loading the same file twice - and that factory *could* be a singleton if you really wanted. – Jon Skeet Jul 03 '13 at 14:31
  • @SotiriosDelimanolis, didn't quite understand what you mean. – mr.nothing Jul 03 '13 at 14:37
  • @mr.nothing I guess I don't understand what your problem is here. – Sotirios Delimanolis Jul 03 '13 at 14:38
  • @JonSkeet, I'm not concerned about reading file twice, but I am about creating this object several times, so that's why I made it a singleton. Could you provide some peace of code to show how it can be rewritten with use of factory pattern? – mr.nothing Jul 03 '13 at 14:40
  • @mr.nothing If the question is about testing singleton, you don't need to if you use Enums. – Sotirios Delimanolis Jul 03 '13 at 14:41
  • @SotiriosDelimanolis, no, the question is about testing `getInstance()` method in any environment designer could have. But as you can see there is LINUX style path hardcoded in class implementation. So test cases written for this method won't work properly at least on Windows. – mr.nothing Jul 03 '13 at 14:44
  • @mr.nothing Have you tried it in windows? The path you've specified will be appended to `C:`. So it will appear as `C:/usr/bin/bar.bar` and that is a path that can be read in Windows. If it exists is another question. You can easily create it. – Sotirios Delimanolis Jul 03 '13 at 14:49
  • @SotiriosDelimanolis, thanks! Will try this, but anyway not a good solution as such a behaviour will have unexpected result on continious integration system with two instances of build launched... – mr.nothing Jul 03 '13 at 14:59

1 Answers1

0

Separate your business logic from the constructional logic. Your class should not care how it is created, leave that to either a factory or DI framework.

Using a DI framework this is very easy:

@Singleton
public final class Foo {
  public Foo(String fileName) { loadBar(fileName);}  // Use in tests
  public Foo() { loadBar(DEFAULT_FILENAME);} 
  private void loadBar(String file)
    {
         // SNIP
    }
}


public class ThingieThatUsesFoo {
     public ThingieThatUsesFoo(Foo foo) { ... } // The DI framework (Guice/Spring) will handle the details of WHAT gets injected.
}

Using a factory is a little more effort, but not a lot.

// You could make FooFactory a singleton if that is your thing.
public class FooFactory {
     private Foo fooInstance = new Foo();
     public Foo getFoo() {
         return fooInstance;
     }
     public void setFooForTestingOnly(Foo fooInstance) { ... } // Set this to a mock or what have thee when testing clients.
}

In both cases testing the client of Foo is easy as you can inject a mock and testing Foo is easier as you can now set the file to read (if you want to be clever don't pass in a file but a Reader or InputStream).

Michael Lloyd Lee mlk
  • 14,561
  • 3
  • 44
  • 81