-1

Ola,

I'm busy writing a unit test like

 monitor.severe(mock(MonitorEventType.class), anyString());

When I execute this I get:

 Invalid use of argument matchers.
 0 matchers expected, 1 recorded.

So I tried:

monitor.severe(mock(MonitorEventType.class), eq(anyString()));

But this gave

Invalid use of argument matchers.
0 matchers expected, 2 recorded.

I also tried to use

monitor.severe(any(MonitorEventType.class), anyString());

but this gives a null pointer.

What works is

   monitor.severe(mock(MonitorEventType.class), "");

But thats not what I want.

My testMethod is :

@Test
public void testSevere() {
    monitor.severe(mock(MonitorEventType.class), eq(anyString()));
    ArgumentCaptor<DefaultMonitoringEventImpl> captor = ArgumentCaptor.forClass(DefaultMonitoringEventImpl.class);
    verify(event).fire(captor.capture());
    DefaultMonitoringEventImpl input = captor.getValue();
    assertThat(fetchMonitorLevel(input), equalTo(MonitorEventLevel.SEVERE.getDescription()));
}

private String fetchMonitorLevel(DefaultMonitoringEventImpl input) {
    Map<String, String> map = input.getMonitorEventWaardes().getWaardenLijst();
    return map.get(MonitorEvent.MONITOR_EVENT_LEVEL_KEY);
}

And the method under test is:

public void severe(MonitorEventType type, String message) {
    write(type, MonitorEventLevel.SEVERE, message, null);
}

@Asynchronous
public void write(MonitorEventType type, MonitorEventLevel level, String message, MonitorEventWaardes pEventWaardes) {
    event.fire(new DefaultMonitoringEventImpl(type, level, message, pEventWaardes));
}

What I want is that When I call monitor.severe with a random MonitorEventType and a random String that the "level" parameter in teh event.fire call is filled with the right value.

Bgvv1983
  • 1,256
  • 1
  • 13
  • 27
  • What is the goal exactly? What do you want to test? – Nicolas Filotto Dec 06 '16 at 10:23
  • show the method `severe` – Nicolas Filotto Dec 06 '16 at 10:24
  • @NicolasFilotto the method severe looks like public void severe(MonitorEventType type, String message) { write(type, MonitorEventLevel.SEVERE, message, null); } here is the complete test public void testSevere() { monitor.severe(mock(MonitorEventType.class), eq(anyString())); ArgumentCaptor captor = ArgumentCaptor.forClass(DefaultMonitoringEventImpl.class); verify(event).fire(captor.capture()); DefaultMonitoringEventImpl input = captor.getValue(); assertThat(fetchMonitorLevel(input), equalTo(MonitorEventLevel.SEVERE.getDescription())); } – Bgvv1983 Dec 06 '16 at 10:26
  • please add it to your question – Nicolas Filotto Dec 06 '16 at 10:28
  • even with it, it is still not clear what you want to achieve. It seems that you want to capture arguments but for what purpose exactly? – Nicolas Filotto Dec 06 '16 at 10:33
  • You want to test that `severe` calls `write` with `MonitorEventLevel.SEVERE` as severity? – Nicolas Filotto Dec 06 '16 at 10:36
  • you will need also to show your class `MonitorEventLevel` – Nicolas Filotto Dec 06 '16 at 10:49
  • @NicolasFilotto See question, I've added the test methoid and the method under test. – Bgvv1983 Dec 06 '16 at 11:32
  • 3
    sorry but you still not answering to all my questions: what do you want to do exactly? what is the method `write`? What is the class `MonitorEventLevel`? it is not possible to answer without all the answers – Nicolas Filotto Dec 06 '16 at 11:46
  • If it doesn't matter what String you put into that method then _that_ unit test is not actually testing _that_ constraint. So just use an instance of an arbitrary String like "". Even if different Strings have different expected results, if you are doing standard unit tests you would test the corner cases. Putting `anyString()` in the act part of the test will not cause JUnit to run a test against every possible String – David Rawson Dec 06 '16 at 21:12
  • @NicolasFilotto I've added some more code. What I want is that When I call monitor.severe with a random MonitorEventType and a random String that the "level" parameter in teh event.fire call is filled with the right value. – Bgvv1983 Dec 07 '16 at 08:12
  • 1
    If you want to test a random string, then send a random string instead of an empty string. BTY using random values in unit test is not a good idea... – Elad Tabak Dec 07 '16 at 08:21
  • @EladTabak "" is something totally different than a random String, as I also explained in my question. – Bgvv1983 Dec 07 '16 at 08:22
  • Then send a random string, or a mock(String.class). I think your question is not clear - are you trying to have the testing framework generate random string for you? or you just want to say "I don't care about the value of the string, as long as I'm verifying that the event was sent properly"? – Elad Tabak Dec 07 '16 at 08:24
  • @EladTabak I've added the question this morning. maybe that explains it a little bit more. Next to this String is a final class and it's not possible to mock final classes. – Bgvv1983 Dec 07 '16 at 08:41
  • What is `event`? What is `DefaultMonitoringEventImpl`? What is `Asynchronous`? It is your duty to provide everything to reproduce – Nicolas Filotto Dec 07 '16 at 11:46
  • @NicolasFilotto dont't think that it doesnt matter for my problem. but for the sake of completeness: DefaultMonitoringEventImpl is just an object. event is from javax.enterprise.event.Event; Asynchronous is from javax.ejb.Asynchronous; – Bgvv1983 Dec 07 '16 at 11:52
  • I can't reproduce how have you defined `event` and `monitor` in your test? – Nicolas Filotto Dec 07 '16 at 15:44
  • @NicolasFilotto InjectMocks private final Monitor monitor = new Monitor(); Cant use the annotation symbol before mock and injectmocks in this comment Mock private Event event; – Bgvv1983 Dec 07 '16 at 19:59
  • Possible duplicate of [Mockito invalid use of Matchers exception](http://stackoverflow.com/questions/35128603/mockito-invalid-use-of-matchers-exception) – David Rawson Dec 08 '16 at 20:43
  • @DavidRawson If you take a good look at the question , comments and the answer below , youll see that is not a duplicate question. – Bgvv1983 Dec 09 '16 at 07:25
  • Bgvv1983 If you take a good look at my comment and that of @EladTabak you'll see the answer is an elaboration of that, but you just weren't paying attention. – David Rawson Dec 09 '16 at 08:07

1 Answers1

3

First, some basics:

  • A mock is a replacement for a real object, that you can create using a mock framework. It records its interactions and verifies them for later.
  • A matcher like any, anyString, or eq tells your mock framework (not your test framework or your system under test) what kind of calls are relevant for stubbing (telling your mock how to behave when its method is called) or verifying (asking your mock framework whether or not a certain method was called).

Most importantly, JUnit and Mockito do not allow for statements like "test this for any input": Statements like any only exist so you can say "when I receive any parameter, take this action" or "check that a method was called with any parameter".

So now your examples:

/* BAD */ monitor.severe(mock(MonitorEventType.class), anyString());

This doesn't work because monitor is real, so anyString is out of place. Your mock is fine there, though, because you're supplying a mock implementation to test a real implementation.

/* BAD */ monitor.severe(mock(MonitorEventType.class), eq(anyString()));

This is the same problem as above, but doubly so: eq should take a real value, never a matcher like anyString.

/* BAD */ monitor.severe(any(MonitorEventType.class), anyString());

Here you've supplied two matchers into a real method call. The matchers are just a signal to Mockito, and a real implementation is processing this, not Mockito.

/*  OK */ monitor.severe(mock(MonitorEventType.class), "");

You're supplying a mocked implementation and a real string to your real system-under-test, so this is a proper use of Mockito, even if it doesn't express what you want to test.


Besides the use of Mockito, the real issue here is that you want to introduce randomness into your test. Even if Mockito were the right tool for the job—it definitely isn't—then your test might pass 90% of the time and fail 10% of the time depending on which input is picked. This will make your tests noisy/flaky at best, and may even lead your team to dismiss the value of testing in general.

Instead, pick representative use cases or edge cases; if you have an enum-type parameter, you can also iterate across all values and run the test once per value. You probably don't need Mockito for this, unless you can't easily create a MonitorEventType instance for some reason; then you might use Mockito to create a fake MonitorEventType for interacting with your real Monitor under test.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thanks! yesterday i found already out that this was my problem, But your post makes it more clear! Is there an other way to achieve what I want? perheps mock(string.class) with Powermockito or just use "", although this is different then I dont care about the actual String input? – Bgvv1983 Dec 09 '16 at 07:23
  • 1
    Even if mocking a String were possible, there is _never_ a good reason to. The alternatives are in the last paragraph: Pick your edge cases manually, or run all cases exhaustively. I suggest using real strings you would find in the wild, as well as `null`. – Jeff Bowman Dec 09 '16 at 10:40