0

I am struggling to Unit test the methods in my web service. All of the methods look at the injected WebServiceContext and pull a userId from that in order to make sure that the user is authorized. I have spent many hours trying to figure out how to mock up the WebServiceContext, but no matter what I try, the context is always null.

My end goal is to be able to return a userId that I specify in my test class so that I can proceed on to test the actual functionality of the rest of the method.

This is a stripped down version of how most of the methods are setup:

@HandlerChain(file = "/handler.xml")
@javax.jws.WebService (...)
public class SoapImpl
{
    @Resource
    private WebServiceContext context;

    public void methodUnderTest()
    {

        // context is NULL here - throws null pointer
        Principal userprincipal = context.getUserPrincipal();

        String userId = userprincipal.getName();


        // Do some stuff - I want to test this stuff, but can't get here
    }

}

This is how I am attempting to mock the context and test

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest(SoapImpl.class)
public class SoapImplTest {

    @Mock
    WebServiceContext context;

    @Mock
    Principal userPrincipal;

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

    @Test
    public void testCreateOverrideRules() {

        SoapImpl testImpl = new SoapImpl();

        when(context.getUserPrincipal).thenReturn(userPrincipal);
        when(userPrincipal.getName()).thenReturn("testUser");

        testImpl.methodUnderTest();

        assertEquals(1,1);
    }

}

I know about dependency injection and passing the context in through the constructor, but I am not sure that I can do that here, because the context is injected through the @resource annotation. A constructor is never called. I don't fully understand how I would implement that.

Also, WebServiceContext and Principal are interfaces, so they cannot be instantiated, which makes this even more confusing. Can anyone help me out here? How can I mock the WebServiceContext and the Principal so I can get past this part of the method and move on to what I really want to test?

UPDATE I was able to solve the problem by using the @InjectMocks annotation as seen in the code below:

@RunWith(MockitoJUnitRunner.class)
@PrepareForTest(SoapImpl.class)
public class SoapImplTest {

    @InjectMocks
    private SoapImpl testImpl = new SoapImpl();

    @Mock
    WebServiceContext context;

    @Mock
    Principal userPrincipal;

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

    @Test
    public void testCreateOverrideRules() {

        when(context.getUserPrincipal).thenReturn(userPrincipal);
        when(userPrincipal.getName()).thenReturn("testUser");

        testImpl.methodUnderTest();

        assertEquals(1,1);
    }

}
mwelk11
  • 643
  • 1
  • 5
  • 10

2 Answers2

1

1) You should have a constructor or setter that sets the context in your class under test. If you don't, you'll want to add one for this exact reason.

2) You don't need to instantiate WebServiceContext or Principal. Simply create mocks for them using Mockito.mock(WebServiceContext.class) and Mockito.mock(Principal.class). Then add Mockito.when(mockWebServiceContext).. to add behavior.

Keep in mind that if you're doing unit testing, you want to test only the logic in your method under test, and not any additional methods or integrations. This is why you want mock instances of both WebServiceContext and Principal. You do not want an integration test (presumably).

java-addict301
  • 3,220
  • 2
  • 25
  • 37
  • I have updated my question with some new code. I added a setter; however, I still can't mock the context because the setter is private. I do not want to make this a public method for security reasons. Do you know how to get around this? – mwelk11 Nov 20 '17 at 15:23
  • Ideally, you would want to use constructor based injection and have a public constructor (instead of a setter) which takes the WebServiceContext. This way, your SoapImpl class will be immutable (a desired characteristic for a stateless system). If you opt for the setter route however, you will need to make the setter public. – java-addict301 Nov 20 '17 at 15:28
  • You will then be able to call the constructor with the Mock object(s). – java-addict301 Nov 20 '17 at 15:29
  • 1
    Turns out I needed to use the @InjectMocks annotation. This allowed me to keep the WebContext as a private class wide variable (without a setter or constructor). The mocked objects were injected into my class and why `Mockito.when()` statements are working as expected. – mwelk11 Nov 20 '17 at 15:57
0

I was able to solve my problem by using the @InjectMocks annotation. This allowed me to inject my mocked objects into the class. The following two resources were helpful in figuring this out:

https://docs.oracle.com/javaee/6/tutorial/doc/bncjk.html

Injecting @Autowired private field during testing

mwelk11
  • 643
  • 1
  • 5
  • 10