2


I am looking for a way to test the data in a class that processes the data and then uploads the data in a manner as clean and portable as possible. So I would like to not resort to making public get functions, to avoid anything being able to reach this data. To keep it portable, I would like to avoid using a test database, so that these tests can be ran in any situation.

Class Foo {     
    private int a;
    private int b;
    private int c;
    private DBSaver dbSaver;

    public Foo(DBSaver dbSaver) {       
        this.dbSaver = dbSaver;
    }

    public void processData() {           
        processData();
        saveToDB();
    }

    private void workNumbers() {     
        a = 1;
        b = 2;
        c = a + b;
    }

    private void saveToDB() {           
        List<Integer> list = new ArrayList<>();
        list.add(a); list.add(b); list.add(c);
        dbSaver.save(list);
    }
}

Test class:

class FooTest {       
    @Mock
    DBSaver dbSaver;
    @Mock
    Foo foo;

    @Test
    private void test() {when(dbSaver.save(any(ArrayList<>.class))).then(returnsFirstArg());
        foo = new Foo(dbSaver);
        Mockito.doThrow(new Exception() {
                //Now I would like to get a, b and c so I can ensure that they have the proper values.
            }
            ).when(foo).processData();
    }
}

Here is a pastebin version of the code]().

workNumbers() will be executed as normal, and since the save() function has been mocked to do nothing, that code won't do anything too serious.

I would like to ask if there is a way to now get the a, b and c values back in list form, through mocking an exception being thrown. The problem is accessing the private values in Foo through the mocked instance in FooTest.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
Lily
  • 35
  • 4

1 Answers1

1

You are getting unit testing with mocking wrong. There is no point in having this:

@Mock
Foo foo;

Foo is your "class under test". You do not want to mock that. If at all, you might want to create a spy here, in order to do partial mocking.

In your case, you don't need to access those private fields. You see, the key point is that your code will call:

dbSaver.save(list);

at some point. Meaning: you can use Mockitoy to verify that a specific list is passed when save() is called on that mocked dbSaver object.

That is the whole point: you have that mocked dbSaver instance, so you are in total control what happens there. As said, you can verify that its methods are called with a specific argument; or you can even use an ArgumentCaptor in order to access the list passed to that method call.

And then you simply ensure that the list contains the expected content (which would be those three values from your class under test!). See here for examples how to do that.

Finally: especially that "exception" part is really wrong. There is no need to generate an exception here. You simply push a mocked dbSaver into an instance of Foo, you call a method on that Foo instance; and then you check for the expected results. That's it; anything else is (useless) over-complication of things.

GhostCat
  • 137,827
  • 25
  • 176
  • 248