0

I want to make a unit test that will ensure that all subclasses implement a method.

Some code may better explain what I am trying to do.

The super class is like this:

class SuperClass
{
    public boolean isImportant()
    {
        throw customException;
    } 
}

Now say that I have sub classes A and B.

I'd like to ensure that this is included in those classes, so like:

class A extends SuperClass
{
    @Override
    public boolean isImportant()
    { 
        return true; //or false
    }
}

and class b:

class B extends SuperClass
{
}

Now if I do B b = new B(); b.isImportant() I will get the exception.

Instead of having a test for each class (there are many and I may miss some and in the future more subclasses may be added), how can I easily test this.

I am thinking if there is some way to instantiate a object of every subclass.

Something like this:

for(Class subClass : SuperClass.subclasses)
{
   subClass obj = new subClass();
   obj.isImportant();
}

As this will iterate through all the subclasses, regardless of if some are added or removed and will fail if there is an exception thrown.

Is there some way to do this?

Mathias Begert
  • 2,354
  • 1
  • 16
  • 26
Aequitas
  • 2,205
  • 1
  • 25
  • 51

3 Answers3

2

Make the method abstract and subclasses have to implement it, or they won't compile. No unit test needed (for that, anyway).

Update

Now, if you can't (or don't want to) change the method to abstract, you can test it like this.

class SuperClass {
    public boolean isImportant() { throw new UnsupportedOperationException(); } 
}

class A extends SuperClass {
    @Override
    public boolean isImportant() { return true; }
}

class B extends SuperClass {
}

class TestIsImportant {

    @SuppressWarnings("unchecked")
    private static final Class<?>[] classesToTest = new Class[] {
        A.class, B.class
    };

    @Test
    public void test() throws Exception {
        for (Class<?> classToTest : classesToTest) {
            classToTest.getDeclaredMethod("isImportant");
        }
    }

}

This will throw NoSuchMethodException if method is not implemented, and it's easy to add to the list of classes to test.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I believe OP's super class has a default implementation, so I'm not sure this will work for him. But this is definitely the correct strategy if you want to ensure subclasses implement a method. – Fred Porciúncula Sep 02 '15 at 23:55
  • 1
    @ThiagoPorciúncula Yeah, my thinking was that if you want to unit test that all your code implements it, why not just enforce it. Unless of course the base class is out of your control, but question doesn't state that as a constraint. – Andreas Sep 02 '15 at 23:56
  • I agree. Unit testing is a great way to ensure that runtime behavior of a unit of code is what you expect. Using an abstract method lets the compiler enforce this rule for you. – Brandon Sep 03 '15 at 00:26
  • 1
    Although this is a very good advise, it does not really **answer** OP's question. – Mathias Begert Sep 03 '15 at 00:32
  • @Andreas in the spirit of staying Objective to the OPs question, I think it would be a better answer to say to make it abstract and that in most cases that is what you will want to do instead of writing unit tests, but then go to explain how to do it anyway. There are always some things that may seem bizarre to a third-party when just hearing about the surface level details, but there could be a completely legitimate reason or corner case to do it. – searchengine27 Sep 03 '15 at 00:33
  • @searchengine27 Done ;-) – Andreas Sep 03 '15 at 00:48
  • is there no way to programatically find the classes? ie. fill in `classesToTest` without having to do it manually? I would like it to work in the future if someone adds another sub class. As to making it abstract, it wasn't originally done as abstract and I assume that it was done this way on purpose for some reason, so I think it would be better to leave it how it was done. – Aequitas Sep 03 '15 at 01:44
  • @Aequitas The link in Thiago Porciúncula's answer says no: [How do you find all subclasses of a given class in Java?](http://stackoverflow.com/questions/492184/how-do-you-find-all-subclasses-of-a-given-class-in-java) – Andreas Sep 03 '15 at 01:45
  • The highest voted answer says you can but it's not easy, and that spring framework makes it easier – Aequitas Sep 03 '15 at 01:47
2

I think what you're trying to test is weird and you should probably go with @Andreas's solution:

Make the method abstract and subclasses have to implement it, or they won't compile. No unit test needed (for that, anyway).

That being said, here's how you'd test what you want:

public class SuperClass {

    public void method() {
        System.out.println("Default implementation.");
    }

}

public class SubClassA extends SuperClass {

    @Override
    public void method() {
        System.out.println("Overridden implementation.");
    }

}

public class SubClassB extends SuperClass {}

public class TestClass {

    @Test
    public void test() throws NoSuchMethodException {
        // nothing happens here
        SubClassA.class.getDeclaredMethod("isImportant");

        // the line below throws NoSuchMethodException, failing the test
        SubClassB.class.getDeclaredMethod("isImportant");
    }

}

Now, if you want to be able to loop through all SuperClass subclasses, you should take a look at this: How do you find all subclasses of a given class in Java?

Community
  • 1
  • 1
Fred Porciúncula
  • 8,533
  • 3
  • 40
  • 57
  • I like this answer the best for explaining that it may not be what he wants to do, but just in case you also tell him how to do it anyway. To make your answer a bit more concise, instead of referencing Andreas answer, can you just reiterate that explanation in your own answer? – searchengine27 Sep 03 '15 at 00:39
  • @searchengine27 Done. – Fred Porciúncula Sep 03 '15 at 00:55
  • 1
    I'm just noticing it now (sorry for all the comments lol), but it may be better to do, in a `@Before` function to call a `SubClassX.getDeclaredMethods()` and put the result in some sort of `TreeSet` and put the classname and `TreeSet` into some sort of key-value pair datastructure (i.e. `Map`), and then in the `@Test`, `Assert` that the function exists in that `Set`, rather than allowing the exception to be thrown. This way, if he was also testing some class `SubClassC` after `SubClassB`, the failure of `SubClassB` won't halt the test. – searchengine27 Sep 03 '15 at 01:11
  • I agree. But the intention was to present the simplest possible example showing how to identify the implementation (or not) of a method from the superclass. It's up to him to absorb that and transform into something more sophisticated, in case he needs it. – Fred Porciúncula Sep 03 '15 at 01:15
  • That link says it's not easy, but doesn't explain it, do you know how? – Aequitas Sep 03 '15 at 01:49
  • If I knew how, I would have added to my answer, that's why I pointed to the most popular question about this in stackoverflow. That's the best discussion you can get about this. Don't read just the accepted answer, read the whole question and the other answers. – Fred Porciúncula Sep 03 '15 at 10:59
0

I don't understand how exactly you think unit testing relates to the inheritance in your case. If you need to ensure that isImportant() is implemented/inherited in the subclasses, simply make A and B extend the SuperClass. Then you can be sure all all subclasses forever will have all the methods of the SuperClass.

class A extends SuperClass {
...

If the method is not implemented in the super class, but you still want to do what you have written, make the SuperClass abstract.

Or finally, define an interface like:

interface SuperInterface {
   boolean isImportant();
}

...and make A and B to implement it. This is probably the cleanest way to do.

TomS
  • 1,159
  • 2
  • 17
  • 35
  • If you look at what the OP asked and how he asked it, you can see why. He seems to be clearly modeling his example after a real-world example in which SuperClass#isImportant() is already implemented in some class, but he is extending it in his own classes. It is probably the case that SuperClass is part of some framework or the standard JDK, and he can't (and if he wanted to build his own custom JDK, probably shouldn't ) make that class abstract. You just told him an answer to a question related to his question, but never answered his question. – searchengine27 Sep 03 '15 at 00:37
  • OK, you are probably right, but in that case, the question should be formulated clearer. I am sorry, but no one should expect anybody to analyze every single word and how it was meant. I did my best to answer what I understood was the problem. – TomS Sep 03 '15 at 00:44
  • I understand. I didn't downvote you because it wasn't an invalid assumption, just explained why I thought your answer wasn't right is all. For all I know, maybe I'm reading between lines that aren't there lol – searchengine27 Sep 03 '15 at 01:04