/* * Copyright (c) 2007 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockito.runners; import java.lang.reflect.InvocationTargetException; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.Filterable; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.mockito.internal.debugging.WarningsCollector; import org.mockito.internal.runners.RunnerFactory; import org.mockito.internal.runners.RunnerImpl; import org.mockito.internal.util.ConsoleMockitoLogger; import org.mockito.internal.util.MockitoLogger; /** * Uses JUnit 4.5 runner {@link BlockJUnit4ClassRunner}. *

* Experimental implementation that suppose to improve tdd/testing experience. * Don't hesitate to send feedback to mockito@googlegroups.com * It is very likely it will change in the next version! *

* This runner does exactly what {@link MockitoJUnitRunner} does but also * prints warnings that might be useful. * The point is that Mockito should help the tdd developer to quickly figure out if the test fails for the right reason. * Then the developer can implement the functionality. * Also when the test fails it should be easy to figure out why the test fails. *

* Sometimes when the test fails, the underlying reason is that stubbed method was called with wrong arguments. * Sometimes it fails because one forgets to stub a method or forgets to call a stubbed method. * All above problems are not immediately obvious. *

* One way of approaching this problem is full-blown 'expect' API. * However it means the 'expectations upfront' business which is not in line with core Mockito concepts. * After all, the essence of testing are explicit assertions that are described consistently at the bottom of the test method. *

* Here's the experiment: a warning is printed to the standard output if the test fails. * Also, you get a clickabe link to the line of code. You can immediately jump to the place in code where the potential problem is. *

* Let's say your test fails on assertion. * Let's say the underlying reason is a stubbed method that was called with different arguments: *


 * //test:
 * Dictionary dictionary = new Dictionary(translator);
 * when(translator.translate("Mockito")).thenReturn("cool framework");
 * String translated = dictionary.search("Mockito");
 * assertEquals("cool framework", translated);
 * 
 * //code:
 * public String search(String word) {
 *     ...
 *     return translator.translate("oups");
 *
 * 
* On standard output you'll see something like that: *

 * [Mockito] Warning - stubbed method called with different arguments.
 * Stubbed this way:
 * translator.translate("Mockito");
 * org.dictionary.SmartDictionaryTest.shouldFindTranslation(SmartDictionaryTest.java:27)
 *  
 * But called with different arguments:
 * translator.translate("oups");
 * org.dictionary.SmartDictionary.search(SmartDictionary.java:15)
 * 
*

* Note that it is just a warning, not an assertion. * The test fails on assertion because it's the assertion's duty to document what the test stands for and what behavior it proves. * Warnings just makes it quicker to figure out if the test fails for the right reason. *

* Note that code links printed to the console are clickable in any decent IDE (e.g. Eclipse). *

* So far I identified 2 cases when warnings are printed: *

  • unsued stub
  • *
  • stubbed method but called with different arguments
  • *

    *
    *

    * Do you think it is useful or not? Drop us an email at mockito@googlegroups.com */ public class ConsoleSpammingMockitoJUnitRunner extends Runner implements Filterable { private final MockitoLogger logger; private RunnerImpl runner; public ConsoleSpammingMockitoJUnitRunner(Class klass) throws InvocationTargetException { this(new ConsoleMockitoLogger(), new RunnerFactory().create(klass)); } ConsoleSpammingMockitoJUnitRunner(MockitoLogger logger, RunnerImpl runnerImpl) { this.runner = runnerImpl; this.logger = logger; } @Override public void run(RunNotifier notifier) { RunListener listener = new RunListener() { WarningsCollector warningsCollector; @Override public void testStarted(Description description) throws Exception { warningsCollector = new WarningsCollector(); } @Override public void testFailure(Failure failure) throws Exception { logger.log(warningsCollector.getWarnings()); } }; notifier.addListener(listener); runner.run(notifier); } @Override public Description getDescription() { return runner.getDescription(); } public void filter(Filter filter) throws NoTestsRemainException { //filter is required because without it UnrootedTests show up in Eclipse runner.filter(filter); } }