Access rights to fields related to JUnit tests

Suppose I have a class that implements a linked list style stack:

public class Stack {
    private StackNode base;

    public void push(Object item) {
        if (base == null) {base = new StackNode(item);}
        // remaining implementation not shown
    }
}

      

and for it the JUnit testing class:

public class TestStack {
    @Test
    public void testPush() {
        Stack st = new Stack();
        st.push(new Integer(3)); 

        assertEquals((Integer)st.base.getContents(), new Integer(3)); //Error!
        // other test cases not shown
    }
}

      

My question is, what is the best practice to solve the test class class access problem for base

?

I know about this question , which discusses the issue of access creation, and I know that I might as well put the code Stack

and TestStack

in the package and make the field package base

- private.

I'm wondering what is the best course of action. I know that I shouldn't use pop

in a push

unit test, and vice versa, since this ties these unit tests together.

I think creating a (public) accessory is bad practice unless I want the client code to access the field , so is this the correct call to make an accessory for the field (or the field itself) -private?

Another informative question discusses options for private methods, but this is for a public method and in order to validate it I need to access a private field, so many statements "if you need to access private methods / classes, you are doing it wrong" doesn't seem to apply here.

+3


source to share


2 answers


We have to write tests that implement the public API. However, this is not always possible. In this case, I would write a test that combines two methods: push

and pop

, because these two create a functional stack:

public class Stack {
    private StackNode base;

    public void push(Object item) {
        ...
    }

    // I know this wasn't part of your code
    public Object pop() {
        ...
    }
}

public class TestStack {
    @Test
    public void testOnePush() {
        // GIVEN
        Stack st = new Stack();

        // WHEN
        st.push(new Integer(3)); 
        Object popped = st.pop();

        assertEquals(popped, new Integer(3));
    }
}

      

If this type of testing is not possible, then you can open it base

directly or through a getter. In this case, I prefer to write a warning comment, i.e. For testing only:



public class Stack {
    StackNode base; // package-private for testing reasons
}

      

You can also warn other developers with method names:

public class Stack {
    private StackNode base;

    /** For unit tests only!!! */
    public StackNode getBaseForTests() {
         return base;
    }
}

      

+4


source


It seems like you've read a lot of questions but haven't gotten to the heart of the message.

The main point: you are not testing internals of your production code. Are you interested in checking the public contract for that. You want to avoid this by checking how to do it.

In other words: if you have a class that implements a stack, then an ideal unit test for such a class ... just tests the behavior of an object of that class. Meaning: You acknowledge that clicking and pop-up values ​​produce the expected results. This occurrence of an empty stack throws an exception ... etc.

If you really want to test, as part, one solution that works without changing the access modifiers is to use Mockito and its @InjectMock annotation.



In other words: you can mock an object StackNode

and @InjectMock does some reflection magic to give you an object Stack

using that mocked StackNode

. Now you can use the Mockito () validation method to make sure this one StackNode

sees the calls you expect to see.

But of course, this means that you are investing a lot of time and energy to write a test that depends entirely on the implementation details Stack

. Once you decide to change it, the whole unit test will break; and you will have to rewrite it completely as well.

And that's why you prefer to check what, not how.

Other than this: it is a common practice that your class and its test class are tested in the same package (not in the same project). And having a packet-protected getter for the field that says // unit test only

is an informal but surprisingly well-working solution.

+1


source







All Articles