Borja
Borja

Reputation: 35

Spy a method from parent class in a class with injected mocks with Mockito

I'm using Mockito in a Java project with Spring and Struts and I'm having problems with testing actions.

I'm not using the Struts2 jUnit plugin to save time with the tests using this approach: Strut 2.3.1.2 Unit test, how to remove Spring codependency vs NPE with getContext().

The problem is when in my action, when getText() is called I've got a NullPointerException.

I'm trying to spy this method, that's inherited from ActionSupport but I don't find a way because the action is annotated with InjectMocks in the test.

Here's a simplied example of the classes:

Parent:

public class ActionSupport {
    public String getText(String  aTextName){
        return this.getTextProvider().getText(aTextName);
    }
}

My action:

public class MyAction extends ActionSupport {
    @Autowired private IMyService myService;

    public String execute(){
        getText("SomeText");
        myService.something();
        return SUCCESS;
    }
}

Test:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
    @Mock private IMyService myService;
    @InjectMocks private MyAction myAction;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void test() {
        myAction.execute();
    }
}

I'm getting this exception:

java.lang.NullPointerException
    at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:167)
    at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:126)
    at com.opensymphony.xwork2.util.TextParseUtil.translateVariables(TextParseUtil.java:48)
    at com.opensymphony.xwork2.util.LocalizedTextUtil.getDefaultMessage(LocalizedTextUtil.java:663)
    at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:534)
    at com.opensymphony.xwork2.util.LocalizedTextUtil.findText(LocalizedTextUtil.java:362)
    at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:208)
    at com.opensymphony.xwork2.TextProviderSupport.getText(TextProviderSupport.java:123)
    at com.opensymphony.xwork2.ActionSupport.getText(ActionSupport.java:103)
    at es.MyAction.execute(MyAction.java:148)

And if I annotate MyAction with @Spy maintaining @InjectMocks I've got an StackOverflowError.

How can I spy only ActionSupport.getText() and let mockito inject mocks on my action?

Upvotes: 1

Views: 2731

Answers (1)

clD
clD

Reputation: 2611

I would avoid using @InjectMocks as it fails silently.

Just add a constructor to your MyAction still using @Autowired i.e. constructor injection. This also helps guarantee required dependencies.

You dont need both initMocks and MockitoJUnitRunner.

public class MyAction extends ActionSupport {

    private IMyService myService;

    @Autowired
    public MyAction(MyService myService) {
        this.myService = myService;
    }

    public String execute(){
        getText("SomeText");
        myService.something();
        return SUCCESS;
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private IMyService myService;

    private MyAction myAction;

    @Before
    public void setup() {
        myAction = spy(new MyAction(myService));
    }

    @Test
    public void test() {

        assertThat(myAction.execute(), equalTo(Action.SUCCESS));

        verify(myAction, times(1)).getText();

        verify(myService, times(1)).something();
    }
}

InjectMocks fails silently

Constructor injection discussion

Upvotes: 3

Related Questions