views:

37

answers:

2

How does one use EasyMock to modify a mocked method's mutable method parameter?

For example, I have class that uses a BlockingQueue. I want to mock the BlockingQueue member for unit testing. My class calls the method queue.drainTo(Collection c). Calling this method removes elements from the queue and adds them to the collection. How would I mock this behavior using EasyMock? Examples would be great.

A: 

hard to tell exactly what your code looks like. Could help you better if I knew the code you want to test.. but assuming your code you want to test looks like this:

private BlockingQueue<String> queue;
private List<String>  myList = new ArrayList<String> ():

private void setBlockingQueue( BlockingQueue<String>  queue ) { 
    this.queue = queue;
}

public List<String> getMyList() {
    return myList;
}

public void setMyList( List<String> myList) {
    this.myList = myList;
}

public void doWork() {
    System.out.println("blah");
    queue.drainTo( myList );
}

A test would be

public void testDoWork() {
    List<String> stuffToDrain = new ArrayList<String>();
    stuffToDrain.add( "foo" );
    stuffToDrain.add( "bar" );
    myTestingClass.setMyList( stuffToTrain );


    BlockingQueue<String> queue = EasyMock.createMock( BlockingQueue.class  );
    myTestingClass.setBlockingQueue( queue );
    queue.drainTo( stuffToDrain );
    EasyMock.replay( queue );
    myTestingClass.doWork();
    EasyMock.verify( queue );



}

Sorry if that isn't right, but really hard to suggest a test for code that I can't see...

Edit - we can't really assert that the mutable param gets changed becuase of how we are using the mock. All we can do is assert that the drainTo method gets called. If drainTo does what we want to do would have to be tested elsewhere.. i.e. in the tests of BlockingQueue.class

Edit 2 - we can be more specific about what list we expect the method to get called with.

bwawok
Sorry for not posting any code but your 'myTestingClass' pretty much nails what I am after. I am looking at your test code. How did the stuffToDrain list get added to myList by running this test?
Nathan Reese
It won't get added to the list. You can't test that here. You would need to test that in a separate test.. (or because BlockingQueue is part of Java, you just assume it is correct and don't test it)
bwawok
ok,is there any way to have EasyMock modify a mutable parameter?
Nathan Reese
I was trying to test the following<code>Object entry = (Object) queue.take(); // blocking calllist.add(entry); queue.drainTo(list, LIST_CAPACITY - 1); // non-blocking call</code>
Nathan Reese
So you have some stuff that happens after the drainTo, and you want to give the test a specific list? Or why do you care what happens to the param? In this test, you assume the queue code is correct, and you want to make sure the other code works right
bwawok
I wanted to test that the fist object taken from the queue was added to the list since I am reading the queue with two calls (one blocking and the other non blocking)
Nathan Reese
But was more curious how to modify a member parameter using EasyMock. If this is not possible that is ok. Just curious
Nathan Reese
I don't think modifying a member parameter fits into unit testing... because you are testing too many things at once.
bwawok
Not sure I agree with that. I have removed the dependency to the external component by using a mock object (thus am only testing my class, i.e. the unit). I should be able to mock all of the behavior of that object getting mocked, including how that object modifies method parameters. Maybe a stub would be a better solution here.
Nathan Reese
I appreciate your help. It is nice to have a forum to talk to others in the field.
Nathan Reese
Well the bahavior of drainTo IS an external dependency. You don't test that in the scope of testing your doWork method.
bwawok
+1  A: 

You can use andAnswer and getCurrentArguments:

public void testDrainToQueue() {
  BlockingQueue<Foo> queue = EasyMock.createMock(BlockingQueue.class);
  EasyMock.expect(queue.drainTo(EasyMock.isA(List.class)))
      .andAnswer(new IAnswer<Integer>() {
        public Integer answer() {
          ((List) EasyMock.getCurrentArguments()[0]).add(new Foo(123));
          return 1; // 1 element drained
        }
      });
  EasyMock.replay(queue);
  ...
}

It sometimes helps to extract a helper class or method:

private static IAnswer<Integer> fakeDrainReturning(final List drainedElements) {
  return new IAnswer<Integer() {
    @Override public Integer answer() {
      ((List) EasyMock.getCurrentArguments()[0]).addAll(drainedElements);
      return drainedElements.size();
    }
  };
}

Then you can do:

List<Foo> drainedElements = Arrays.asList(new Foo(123), new Foo(42));
EasyMock.expect(queue.drainTo(EasyMock.isA(List.class)))
    .andAnswer(fakeDrainReturning(drainedElements));

It might be better to use a real BlockingQueue and find a way to insert the desired value into the queue before the method that you expect to remove data from the queue.

NamshubWriter