0

I have a simple class, that contains a list:

public class SomeClass {

    private AppDataSource appDataSource; // it's interface
    private List<Object> someList;

    ////

    public List<Obejct> loadSomeList() {
        if (someList == null) {
            return appDataSource.getListFromDatabase();
        }
        retrunf someList;
    }
}

The point is - i want that list to be loaded from DB only once. And i want to unit test this function. I am noob in TDD and all i could do - write a public getter and setter for someList and use them in unit test. But it's conceptually wrong - i don't want class's clients use this member variable directlty.

How can i properly test method in this situation?

6 Answers6

6

You are getting unit testing wrong.

Unit testing is about testing behavior of your classes; not implementation details.

You don't test if private fields do have this or that content. The only thing you test is that methods do what they are supposed to do.

That of course means that your class must have ways to insert "special lists" for testing.

Long story short: you want to step back, and spent the next 2, 3 hours learning how to write "easy to test code"; for example by watching the great videos from Google Tech on CleanCode .

GhostCat
  • 127,190
  • 21
  • 146
  • 218
1

You should mock your list before your tests are coming.Use @Before for initialize your list.

private List<Object> someList;

@Before 
 public void initialize() {
    someList = new ArrayList<Object>();
    someLisi.add(..);
    someList.add(..);
 }

And test your method using this mock list.You can also use @BeforeClass for mocking your list. You can read differences between @Before and @BeforeClass here

Community
  • 1
  • 1
Sai Ye Yan Naing Aye
  • 6,218
  • 10
  • 42
  • 63
  • Doesn't make much sense - the code you are "quoting" is part of his **class under test**. So you are actually telling him to set his private fields from outside his class; which is of course not going to work. – GhostCat Jun 23 '16 at 11:41
  • @Jägermeister thanks for comment. I thought OP show his test class and I answered like that.Now I read the question again and edit my answer. – Sai Ye Yan Naing Aye Jun 23 '16 at 12:19
0

You could initialize your private fields through constructor in test fixture. It's the most common way I guess.

Another option is to write tests in Groovy which can directly access private fields in Java classes. So you don't need to give an access to your private fields.

Oleg L.
  • 78
  • 1
  • 4
0

You are able to test your loadSomeList() like this:

public class SomeClass {

    private List<Object> someList;

    public List<Object> loadSomeList() {
        if (someList == null) {
            someList = new ArrayList<>();
            someList.add(new Object());
            return someList;
        }
        return someList;
    }

    public List<Object> getSomeList() {
        return someList;
    }

    public void setSomeList(List<Object> someList) {
        this.someList = someList;
    }

The Test Class should have two tests:

  • In the first test you can test if you have a new List. Your create a new instance of SomeClass and call your someClass.loadSomeList() method. If the list is not null the test is ok.
  • The second test you can test if your someClass instance already has a list. In the test your just add one object in your list and set it to someClass.

public class SomeClassTest {

    @Test
    public void testLoadSomeListNewList() {
        SomeClass someClass = new SomeClass();
        List<Object> list = someClass.loadSomeList();
        assertNotNull(list);
    }

    @Test
    public void testLoadSomeListGivenList() {
        SomeClass someClass = new SomeClass();
        List<Object> list = new ArrayList<>();
        list.add(new Object());
        someClass.setSomeList(list);

        someClass.loadSomeList();
        assertTrue(someClass.getSomeList().size() == 1);
    }
Patrick
  • 10,322
  • 12
  • 63
  • 100
0

If you create a package in the test directory with the same name of the package of your class, and if you put the field in protected you will be able to access the field directly

Jon
  • 32
  • 2
  • 3
    @ЕвгенийКравцов Its a bad practice to have a protected field as class variable. [Lookup](http://programmers.stackexchange.com/questions/162643/why-is-clean-code-suggesting-avoiding-protected-variables). Every other class in this package has now access to your field and able to change its value. You cant be sure anymore if the value has changed anywhere in your code. – Patrick Jun 23 '16 at 09:00
0

Rather than exposing and testing the list, change your appDataSource so that you can set it from outside of the class. Make it an interface that provides the getListFromDatabase() method. Then for testing, pass in a mock datasource that implements the interface and has a counter that you can query to tell you how many times the getListFromDatabase method was called.

Look at what you want to test, and then work towards that. You didn't mention in your criteria that the list itself was important. What was important was how many times you query the database.

Shadowfen
  • 395
  • 1
  • 8