Is it possible to perform unit-class testing with inheritance?

2

I need to do unit test of PlanilhaReader but this extends ArrayList<String> and I override the contains method. Is there any way to run it?

@Component
public class PlanilhaReader extends ArrayList<String> {

    final String UPLOAD_PATH = PropertiesSingleton.getValue("api.upload.dir");

    @Override
    public boolean contains(Object o) {
        String paramStr = (String)o;
        for (String s : this) {
            if (paramStr.equalsIgnoreCase(s)) return true;
        }
        return false;
    }

    public BasicDBList readMetadados(Planilha planilha) {...}
}

Test

public class PlanilhaReaderTest {

    @Test
    public void testReadMetadados() throws Exception {
        PlanilhaReader reader = new PlanilhaReader();
        Planilha planilha = new Planilha().setPath("552d4cf1ccf2c0a58301a744/55163343b0df070bbc66e1bb6e0c3f9b.xlsx");
        reader.readMetadados(planilha);
    }
}

When compiling I get the error:

Hot Swap failed
         Tomcat 8.0.18: hierarchy change not implemented; 
         Tomcat 8.0.18: Operation not supported by VM; 
         PlanilhaReaderTest: hierarchy change not implemented; 
         PlanilhaReaderTest: Operation not supported by VM; 
         PlanilhaReaderTest: hierarchy change not implemented; 
         PlanilhaReaderTest: Operation not supported by VM
    
asked by anonymous 16.04.2015 / 15:15

1 answer

2

Inheriting from ArrayList<String> is bad. In fact inheriting from any class of Collection , unless you know very well what you are doing is bad. The reason is that the code of Collection s is out of your control and inheriting from one of them something bad can happen .

Is it possible to test your subclass properly? Yes. Is it easy to do that? No. The reason is that your subclass depends heavily on the superclass. In your case, in doing so, your Collection may create some unexpected behaviors. For example:

if (lista.contains("abacaxi")) lista.remove("abacaxi");

This will mysteriously fail if what is on your list is "PINEAPPLE" instead of "pineapple". This is because the behavior of the superclass has some implicit conditions that the subclass innocently ends up violating.

In fact you can fix your subclass. But in practice you will have to introduce a lot of distinct changes and will eventually rewrite the entire superclass within the subclass and probably not what you want.

On the other hand, you'll probably argue that you only use this class for a specific purpose and that you do not need all inherited superclass workings. In this case, what you inherit from the superclass is called damn inheritance , behaviors that are not desired in the subclass and are there because they came from the superclass.

What is the solution to this? Abandoning inheritance and using composition:

@Component
public class PlanilhaReader {

    final String UPLOAD_PATH = PropertiesSingleton.getValue("api.upload.dir");

    private final List<String> lista;

    public PlanilhaReader() {
        lista = new ArrayList<>(10);
    }

    public boolean contains(String paramStr) {
        return this.lista.contains(paramStr.toLowerCase(Locale.ROOT));
    }

    public void add(String s) {
        this.lista.add(s.toLowerCase(Locale.ROOT));
    }

    public BasicDBList readMetadados(Planilha planilha) {...}
}

And as a result, your class is now much easier to test. The number of interactions between the data and between the methods of this class is much smaller. It is also much more difficult to use it improperly or unexpectedly.

Note that for simplicity, I've decided to put all the elements in the already converted list into a canonical form, in order to make the original contains work.

    
16.04.2015 / 15:42