funbee / mockito

Automatically exported from code.google.com/p/mockito
0 stars 0 forks source link

Custom method invocations #5

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I ran into this problem... can you help?

The listeners to one class are generated from another (I could use a
listener factory, but...)

I'd like to get a custom invocation to allow me to grab hold of the
listeners. Any way of doing that?

Here's an example of what I want, hope it explains a bit better...

  @Test
  public void wibbleSubjectCollectorShouldMaintainListOfSubjects() {
    Subject subject1 = Mockito.mock(Subject.class);
    Subject subject2 = Mockito.mock(Subject.class);

Mockito.stubVoid(subject1).to(catchTheListenerSoICanPretendToRespondToIt).on().s
etObserver(any(Observer.class));
  }

  public static class WibbleSubjectCollector {
    private Set<Subject> subjectsWithWibble = new HashSet<Subject>();

    public WibbleSubjectCollector(Subject... subjects) {
      for (final Subject subject : subjects) {
        subject.setObserver(new Observer() {
          public void changed(String message) {
            if (message.equals("Wibble")) {
              subjectsWithWibble.add(subject);
            } else {
              subjectsWithWibble.remove(subject);
            }
          }
        });
      }
    }
  }

  public static interface Observer {
    public void changed(String message);
  }

  public static class Subject {
    private Observer observer;
    private String message = "";

    public void changeMe(String message){
      this.message = message;
      observer.changed(message);
    }

    public void setObserver(Observer observer) {
      this.observer = observer;
    }
  }

Original issue reported on code.google.com by featheredwings@gmail.com on 13 Mar 2008 at 4:29

GoogleCodeExporter commented 9 years ago
What would you like to do with the observer, then ? 
What the test should prove ?

Original comment by iczechowski@gmail.com on 13 Mar 2008 at 10:42

GoogleCodeExporter commented 9 years ago
>catchTheListenerSoICanPretendToRespondToIt

How do you want to respond to setObserver() ?

setObserver() is a void method that changes state. Testing it means asserting 
state
of the Subject. I would expose the state of Subject via getters and assert it:

  public void wibbleSubjectCollectorShouldMaintainListOfSubjects() {
    Subject subject1 = new Subject();
    ...
    assertEquals(..., subject1.getObserver());
  }

Closing the issue for now (to clear the backlog - don't get false impression 
that we
don't want to help!) 

Let us know you think and reopen if you feel like.

Original comment by szcze...@gmail.com on 16 Mar 2008 at 2:13

GoogleCodeExporter commented 9 years ago

Original comment by szcze...@gmail.com on 16 Mar 2008 at 2:22

GoogleCodeExporter commented 9 years ago
So, just to stop using WibbleCode for a sec... it's the same parking lot 
problem. My
mock is a lot; my attendant is observing multiple lots. So the lot tells the
anonymous observers if anyone's parked in them, and if they're full the 
observers
remove them from the list of available parking lots.

I just want to call the method on the observer. So I want to respond to 
setObserver
with something like:

// Given two lots and an attendant

ParkingLot lot1 = mock(ParkingLot.class);
ParkingLot lot2 = mock(ParkingLot.class);

ArrayList params = new ArrayList();
stubVoid(lot1).will(captureParamsTo(params)).on().setObserver(anyObject());

Attendant attendant = new Attendant(lot1, lot2);

ParkingLotObserver observer = params.get(0);

// Given one of the lots is full

observer.parked(10, 10) <-- that's the capacity and the number of cars

// When we ask the attendant to park the car
attendant.park();

// Then he should park in the other lot.
verify(lot2).park();

The problem is getting hold of the observer to do the observer.parked(10, 10) 
when
the observer is an anonymous class. The only ways I've found round this so far 
are:

- Roll your own mocks
- Don't use anonymous classes - make the Attendant an observer and pass each 
lot into
the parked(...) method
- Add a factory for the observers, and mock that too.

I went with the first approach; the other two make my code less elegant (IMHO) 
just
for the tests.

Original comment by featheredwings@gmail.com on 22 Mar 2008 at 1:29

GoogleCodeExporter commented 9 years ago
Interesting. Can you paste some more code (implementations of ParkingLot,
ParkingLotObserver and Attendant classes would be great)?

Looking at the code, I think I would go for own mocks, too.

IMHO, this line seems to be overspecify the test (assuming that you want to 
prove
that when lot1 is full then the attendant should park at lot2):

observer.parked(10, 10)

Those lines seem to spam the test (assuming that you are not trying to prove 
that
setting observers works):

ArrayList params = new ArrayList();
stubVoid(lot1).will(captureParamsTo(params)).on().setObserver(anyObject());

ParkingLotObserver observer = params.get(0);

Original comment by szcze...@gmail.com on 22 Mar 2008 at 4:53

GoogleCodeExporter commented 9 years ago
I can send these to you... I'd rather not put the implementations online, as it 
would
make a solution to one of the TWU exercises available! Hope that's OK.

Original comment by featheredwings@gmail.com on 23 Mar 2008 at 9:03

GoogleCodeExporter commented 9 years ago
Hi, finally found some time to look at it.

Yes if you're still interested in investigating that, send me some code to my 
email.

I guess, If you just want to capture the args you can implement argument 
matcher that
will always evaluate to true but will remember the argument passed. Then the 
code
would look like that:

{{{
        Object object = new Object();
        mock.simpleMethod(object);

        IsCaptured isCaptured = new IsCaptured();
        verify(mock).simpleMethod(argThat(isCaptured));

        assertSame(object, isCaptured.getArgument());
}}}

Original comment by szcze...@gmail.com on 14 Apr 2008 at 10:21

GoogleCodeExporter commented 9 years ago

Original comment by szcze...@gmail.com on 19 Apr 2009 at 7:40