whoami
whoami

Reputation: 1627

How to inject the variable in the abstract class while unit testing the subclass?

I have an abstract class BaseTemplate and multiple classes extending it. In one of the concrete class(SmsTemplate extends BaseTemplate), we have a private variable Gson. We have the same private variable(Gson) in the abstract class as well.

While unit tesing the concrete class, methods in the abstract class is getting called from the concrete class. In my Unit test, I am using Whitebox.setInternalState(smsTemplateObj, gsonObj); to inject the Gson object into the private members of SmsTemplate and BaseTemplate but the Gson is getting injected only in the subclass. In abstract class, its NULL, meaning not injected. Below is the implementation.

Can someone please tell how to inject the Gson object in the abstract class?

abstract class BaseTemplate{

    private Gson gson;//Here its not getting injected

    protected String getContent(Content content){
        return gson.toJson(content); // ERROR - gson here throws NPE as its not injected
    }
}

class SmsTemplate extends BaseTemplate{

    private Gson gson;//Here its getting injected

    public String processTemplate(Content content){
        String strContent = getContent(content);
        ...
        ...
        gson.fromJson(strContent, Template.class);
    }
}

Upvotes: 4

Views: 4257

Answers (2)

Fred Porciúncula
Fred Porciúncula

Reputation: 8902

Whitebox.setInternalState() method will only set the value of the first field it encounters going up through the hierarchy of the object you pass. So once it finds gson field in your subclass, it won't look further and won't change the superclass field.

There are two solutions for this case:

  • Change the variables names. If the variables have different names, you can simply invoke Whitebox.setInternalState() twice, one for each variable.
  • Set the field manually using reflection. You can also just set the field without Mockito's help using something like the following snippet.

Snippet:

Field field = smsTemplateObj.getClass().getSuperclass().getDeclaredField("gson");
field.setAccesible(true);
field.set(smsTemplateObj, gsonObj);

Upvotes: 7

bryce
bryce

Reputation: 852

You need a second abstraction layer:

abstract class BaseTemplate{

    // private Gson gson;// no Gson here

    protected String getContent(Content content){
        // do what you want without Gson
    }
}

abstract class BaseTemplateWithGson extends BaseTemplate{

    protected Gson gson;

    @Override
    protected String getContent(Content content){
        return gson.toJson(content);
    }
}

class SmsTemplate extends BaseTemplateWithGson {

    public String processTemplate(Content content){
        String strContent = getContent(content);
        ...
        ...
        gson.fromJson(strContent, Template.class);
    }
}

Upvotes: 0

Related Questions