views:

32

answers:

1

SO I have a kinda convoluted problem I will describe it with similar examples instead of copying TONS of code in. The problem comes in that I don't really know how to use JMockit well, dare I say properly.

I have an interface:

public interface IRsrcResolver {
 RsrcSet getForQuery(Query, RsrcContext, int);
}

There is a real implementation of it - it is kinda intense. So I made a mocked version that just returns null. I want to then set an [nonstrict]expectation that will return the proper values. It changes test to test.

In code:

public class MockRsrcResolver implements IRsrcResolver { 
  RsrcSet getForQuery(Query, RsrcContext, int) { return null; } 
}

in the Test class I implement it and then want set the expectations (StateEngine uses the RsrcResolver)

    public class StateEngineTest {
      @Mocked
      public IRsrcResolver rsrcResolver;
      @Mocked
      public RsrcSet rsrcSet;

      @Mocked
      private StateEngine stateEngine;


      public void setUp () throws Exception {
        stateEngine = new StateEngine ();
        rsrcResolver = new MockRsrcResolver ();
        rsrcSet = new RsrcSet ();

        new NonStrictExpectations () {
          {
            rsrcResolver.getForQuery((Query) any, (RsrcContext)any, anyInt);
            result = rsrcSet;
          }
        }
      }


    @Test
    public void testSetState () {
      /*... does something that calls getForQuery (...) on an IRsrcResolver ...*/
      stateTest.doSomething ();
      /* ... assert assert assert ... */
    }  
  }

In doSomething there is something like:

  IRsrcResolver m_rsrcResolver = new RsrcResolver ()
  RsrcSet m_rsrcSet = m_rsrcResolver.getQuery (query, rsrcCtx, 4);
  m_rsrcSet.doSomethingElse ();

m_rsrcSet gets a null value and the debug shows it calling into the null MockRsrcResolver, instead of using the expectation... Am I incorrect in doing it this way? Thanks for any advice you may have!

A: 

JMockit is not really easy to understand, because there is a lot of magic happening when we use it, and this can be very unintuitive (though very powerful).

In your situation, it appears that the class MockRsrcResolver is useless, since you want to mock a call to a method of RsrcResolver (which is instanciated in the tested code).

There is a misuse of the expectation's mocked variables. rsrcResolver, rsrcSet and stateEngine should not be given a value. They are actually placeholders for the mocked instances that JMockit magically generates.

Solution 1

Here is how I would have mocked this method, if RsrcSet doesn't have to be mocked :

final RsrcSet queryResult = new RsrcSet();
// (insert initialization of queryResult here)

new NonStrictExpectations () {
  RsrcResolver rsrcResolver;
  {
    rsrcResolver.getForQuery((Query) any, (RsrcContext)any, anyInt);
    result = queryResult
  }
}

Note that I declare the "mock placeholder" variables as member of the expectation, not as member of the test class. Maybe the latter works too, I don't know (there are many ways to achieve the same result with JMockit). But the important point is that I don't reassign those variables.

The above expectation means : "intercept every calls of the method RsrcResolver#getForQuery and return the queryResult instance instead". Actually I'm not sure about the precise behavior, when using non-strict expectations (I mainly use strict ones). Maybe it means : "intercept calls of every methods of RsrcResolver, doing nothing and returning default values. But for the special case of method RsrcResolver#getForQuery, return the queryResult instance."

Solution 2

Here is how I would have mocked this method, if RsrcSet#doSomethingElse has to be mocked too :

new NonStrictExpectations () {
  RsrcResolver rsrcResolver;
  RsrcSet rsrcSet;
  {
    rsrcResolver.getForQuery((Query) any, (RsrcContext)any, anyInt);
    result = rsrcSet;
    rsrcSet.doSomethingElse();
  }
}

The above expectation means : "intercept every calls of the method RsrcResolver#getForQuery and return a mocked RsrcSet instance instead. This mocked instance will do nothing when called". Of course, if doSomethingElse has to return a value, you can use a result = instruction.

barjak