Standard way to capture arguments in JMock
Asked Answered
E

3

8

Is there an already built-in, standard way in JMock to capture method arguments to test the argument object later on with standard JUnit functionality?

Something like

final CapturedContainer<SimpleMailMessage>capturedArgumentContainer = new ...
context.checking(new Expectations() {{
    oneOf(emailService.getJavaMailSender()).send(
       with(captureTo(capturedArgumentContainer)));
}});

assertEquals("helloWorld", capturedArgumentContainer.getItem().getBody());

CapturedContainer and captureTo do not exist — they are what I'm asking for.

Or do I need to implement this on my own?

Ephialtes answered 17/6, 2011 at 12:39 Comment(0)
G
4

I think you're missing the point a little here. The idea is to specify in the expectation what should happen, rather than capturing it and checking later. That would look like:

context.checking(new Expectations() {{
    oneOf(emailService.getJavaMailSender()).send("hello world");
}});

or perhaps, for a looser condition,

context.checking(new Expectations() {{
    oneOf(emailService.getJavaMailSender()).send(with(startsWith("hello world")));
}});
Gimble answered 24/6, 2011 at 22:57 Comment(5)
you are right in general. But sometimes it is to complicated to use (and produce very unreadable code). For example in case of my example: the argument is an complex object, and I need to check only one attribute (body). So the code will become a mass.Ephialtes
I accept your answer because I understand your answer this way: There is no standard way to capture arguments, because this is not the way it should work.Ephialtes
actually, checking one attribute from an object is quite easy. oneOf(emailService).send(with(hasProperty("foo", equalTo("bar")));Gimble
I disagree: if your tested method creates an object, uses it in two calls and returns it, you might want to check that the object used in both calls and the object returns are all the same object. For this, I don't see another way than being able to capture the object.Tati
That's true, but makes me wonder about the design. A concrete example would be really handy about now...Gimble
P
11

You can do this by implementing a new Matcher that captures the argument when match is called. This can be retrieved later.

class CapturingMatcher<T> extends BaseMatcher<T> {

  private final Matcher<T> baseMatcher;

  private Object capturedArg;

  public CapturingMatcher(Matcher<T> baseMatcher){
    this.baseMatcher = baseMatcher;
  }

  public Object getCapturedArgument(){
    return capturedArg;
  }

  public boolean matches(Object arg){
    capturedArg = arg;
    return baseMatcher.matches(arg);
  }

  public void describeTo(Description arg){
    baseMatcher.describeTo(arg);
  }
}

Then you can use this when setting up your expectations.

final CapturingMatcher<ComplexObject> captureMatcher 
  = new CapturingMatcher<ComplexObject>(Expectations.any(ComplexObject.class));

mockery.checking(new Expectations() {{
      one(complexObjectUser).registerComplexity(with(captureMatcher));
}});

service.setComplexUser(complexObjectUser);

ComplexObject co = 
  (ComplexObject)captureMatcher.getCapturedArgument();

co.goGo();
Phospholipide answered 2/12, 2011 at 15:42 Comment(0)
G
4

I think you're missing the point a little here. The idea is to specify in the expectation what should happen, rather than capturing it and checking later. That would look like:

context.checking(new Expectations() {{
    oneOf(emailService.getJavaMailSender()).send("hello world");
}});

or perhaps, for a looser condition,

context.checking(new Expectations() {{
    oneOf(emailService.getJavaMailSender()).send(with(startsWith("hello world")));
}});
Gimble answered 24/6, 2011 at 22:57 Comment(5)
you are right in general. But sometimes it is to complicated to use (and produce very unreadable code). For example in case of my example: the argument is an complex object, and I need to check only one attribute (body). So the code will become a mass.Ephialtes
I accept your answer because I understand your answer this way: There is no standard way to capture arguments, because this is not the way it should work.Ephialtes
actually, checking one attribute from an object is quite easy. oneOf(emailService).send(with(hasProperty("foo", equalTo("bar")));Gimble
I disagree: if your tested method creates an object, uses it in two calls and returns it, you might want to check that the object used in both calls and the object returns are all the same object. For this, I don't see another way than being able to capture the object.Tati
That's true, but makes me wonder about the design. A concrete example would be really handy about now...Gimble
K
0

I found myself in a similar situation wanting to check a field of an object passed into a mock. Instead of using a capturing matcher as Mark illustrates I tried what I consider the more JMock way of doing things. Code adjusted for your use case:

mockery.checking(new Expectations() {{
  oneOf(emailService.getJavaMailSender()).send(
    with(Matchers.<SimpleMailMessage>hasProperty("body", equal("Hello world!"))));
}});

I understand that this has limitations but hamcrest matchers should be able to test the object in question sufficiently in most cases. Hope this helps.

Kwapong answered 15/3, 2016 at 9:38 Comment(1)
Is there a way (following this style) to test multiple properties?Vav

© 2022 - 2024 — McMap. All rights reserved.