How to test protected methods of abstract class using JUnit and JMock
Asked Answered
E

3

5

I have such situation - I have interface (say MyInterface) and simple partial implementation (AbstractMyInterface). The latter adds a few protected methods which I would like to test.

Currently I simply write by hand a mock object which extends AbstractMyInterface and export protected methods as public. Is there a simpler way of doing this - for example using JMock+scripting?

Episodic answered 4/12, 2011 at 13:15 Comment(0)
S
10

I cannot see any problem with testing protected methods with JUnit. As long as package structure for tests mirrors source tree structure, methods other than private are visible for tests.

Of course if implementation to test is abstract, you have to create normal subclass of class under test by yourself (or do it via some mocking library if fits better for your purposes). Also in such a case, no need to create layer of public methods for just for calling protected visibility methods. Only for private methods this strategy does not work. But often need to test private methods is sign of design problem anyway.

For example: Class to test located to src/mypackage/AbstractClass.java package mypackage;

/** This could as well implement some interface, 
    but that does not change a thing */
public abstract class AbstractClass {
    protected int returnsOne() {
        return 1;
    }
}

And test which is located to tests/mypackage/AbstractClassTest.java

package mypackage;

import org.junit.Test;

import static junit.framework.Assert.assertEquals;

public class AbstractClassTest {
    @Test
    public void returnsOneReturnsOne() {
        AbstractClass instanceToTest = new AbstractClassTestable();
        assertEquals(1, instanceToTest.returnsOne());
    }
}

/** This is needed, because we cannot construct abstract class directly */
class AbstractClassTestable extends AbstractClass {
}
Seedcase answered 4/12, 2011 at 17:40 Comment(3)
There is no problem except that I have growing list of unused stubs in test.Episodic
in your code snippet, I think you forgot to add abstract modifier to your AbstractClass :)Astri
Instead of using an inner class, you can simply initialize the instanceToTest as new AbstractClass(){};Astri
U
3

Just a suggestion,

What if we don't test the protected methods, can we use the public methods to cover those protected methods?

If not, is it because of the protected methods too complicated, refactor to extract the complicated things to a new object, which provides public interfaces, leaving the old object just one private object in some public methods.

The test will later be on the new object.

This blog post might be helpful.

Underhand answered 5/12, 2011 at 15:30 Comment(4)
No - the protected methods are quite simple but they are tided in to public API (part of simple publish/subscribe model). The part is used by public methods but to test if it works I need access to protected part. As of post - while I agree that private methods should not be tested but protected methods are used by others.Episodic
Public methods needs to access public APIs, which wrapped by protected methods, seems this class has two missions. Design a wrapper class to hide the public APIs, and a user class to use the service provided by the wrapper. So, even when the APIs is going to be changed, no harm to user class which may full of logics. What I learned from doing unit test for messy ten years lifetime c code is if it's hard to do unit test, leave it, refactor the code is a easy and a good RoI way.Underhand
Chris Zheng: Could you show that for simple code that uses publish subscribe? The public methods should just register/deregister observers while the protected method just fire various events (looping over observers set and sent notification events). I fail to see solution which would do it in way you describe and would not violate KISS rule.Episodic
OK, I get it. If this is all what you have, make a new class seems not simple. But it is so simple that can test through public interfaces. Mock this object, and test through public interface, check whether or not the notification events are sent.Underhand
D
0

you can make an abstract test case for the interface (or abstract class). then make a concrete test case that extends your abstract test case for each concrete implementation of your interface (or abstract class).

Dansby answered 4/12, 2011 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.