blob: 637f9327ca9eb8a4282e26ddc4c26f9c63c7e89f [file] [log] [blame]
/*
* 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 <b>JUnit 4.5</b> runner {@link BlockJUnit4ClassRunner}.
* <p>
* Experimental implementation that suppose to improve tdd/testing experience.
* Don't hesitate to send feedback to mockito@googlegroups.com
* <b>It is very likely it will change in the next version!</b>
* <p>
* 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.
* <p>
* 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.
* <p>
* 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 <b>explicit assertions</b> that are described consistently at the <b>bottom of the test</b> method.
* <p>
* 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.
* <p>
* Let's say your test fails on assertion.
* Let's say the underlying reason is a stubbed method that was called with different arguments:
* <pre class="code"><code class="java">
* //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");
*
* </code></pre>
* On standard output you'll see something like that:
* <pre class="code"><code class="java">
* [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)
* </code></pre>
* <p>
* 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.
* <p>
* Note that code links printed to the console are clickable in any decent IDE (e.g. Eclipse).
* <p>
* So far I identified 2 cases when warnings are printed:
* <li>unsued stub</li>
* <li>stubbed method but called with different arguments</li>
* <p>
* <br/>
* <p>
* 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);
}
}