1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5package org.mockito.runners; 6 7import java.lang.reflect.InvocationTargetException; 8 9import org.junit.runner.Description; 10import org.junit.runner.Runner; 11import org.junit.runner.manipulation.Filter; 12import org.junit.runner.manipulation.Filterable; 13import org.junit.runner.manipulation.NoTestsRemainException; 14import org.junit.runner.notification.Failure; 15import org.junit.runner.notification.RunListener; 16import org.junit.runner.notification.RunNotifier; 17import org.junit.runners.BlockJUnit4ClassRunner; 18import org.mockito.internal.debugging.WarningsCollector; 19import org.mockito.internal.runners.RunnerFactory; 20import org.mockito.internal.runners.RunnerImpl; 21import org.mockito.internal.util.ConsoleMockitoLogger; 22import org.mockito.internal.util.MockitoLogger; 23 24/** 25 * Uses <b>JUnit 4.5</b> runner {@link BlockJUnit4ClassRunner}. 26 * <p> 27 * Experimental implementation that suppose to improve tdd/testing experience. 28 * Don't hesitate to send feedback to mockito@googlegroups.com 29 * <b>It is very likely it will change in the next version!</b> 30 * <p> 31 * This runner does exactly what {@link MockitoJUnitRunner} does but also 32 * prints warnings that might be useful. 33 * The point is that Mockito should help the tdd developer to quickly figure out if the test fails for the right reason. 34 * Then the developer can implement the functionality. 35 * Also when the test fails it should be easy to figure out why the test fails. 36 * <p> 37 * Sometimes when the test fails, the underlying reason is that stubbed method was called with wrong arguments. 38 * Sometimes it fails because one forgets to stub a method or forgets to call a stubbed method. 39 * All above problems are not immediately obvious. 40 * <p> 41 * One way of approaching this problem is full-blown 'expect' API. 42 * However it means the 'expectations upfront' business which is not in line with core Mockito concepts. 43 * After all, the essence of testing are <b>explicit assertions</b> that are described consistently at the <b>bottom of the test</b> method. 44 * <p> 45 * Here's the experiment: a warning is printed to the standard output if the test fails. 46 * 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. 47 * <p> 48 * Let's say your test fails on assertion. 49 * Let's say the underlying reason is a stubbed method that was called with different arguments: 50 * <pre class="code"><code class="java"> 51 * //test: 52 * Dictionary dictionary = new Dictionary(translator); 53 * when(translator.translate("Mockito")).thenReturn("cool framework"); 54 * String translated = dictionary.search("Mockito"); 55 * assertEquals("cool framework", translated); 56 * 57 * //code: 58 * public String search(String word) { 59 * ... 60 * return translator.translate("oups"); 61 * 62 * </code></pre> 63 * On standard output you'll see something like that: 64 * <pre class="code"><code class="java"> 65 * [Mockito] Warning - stubbed method called with different arguments. 66 * Stubbed this way: 67 * translator.translate("Mockito"); 68 * org.dictionary.SmartDictionaryTest.shouldFindTranslation(SmartDictionaryTest.java:27) 69 * 70 * But called with different arguments: 71 * translator.translate("oups"); 72 * org.dictionary.SmartDictionary.search(SmartDictionary.java:15) 73 * </code></pre> 74 * <p> 75 * Note that it is just a warning, not an assertion. 76 * The test fails on assertion because it's the assertion's duty to document what the test stands for and what behavior it proves. 77 * Warnings just makes it quicker to figure out if the test fails for the right reason. 78 * <p> 79 * Note that code links printed to the console are clickable in any decent IDE (e.g. Eclipse). 80 * <p> 81 * So far I identified 2 cases when warnings are printed: 82 * <li>unsued stub</li> 83 * <li>stubbed method but called with different arguments</li> 84 * <p> 85 * <br/> 86 * <p> 87 * Do you think it is useful or not? Drop us an email at mockito@googlegroups.com 88 */ 89public class ConsoleSpammingMockitoJUnitRunner extends Runner implements Filterable { 90 91 private final MockitoLogger logger; 92 private RunnerImpl runner; 93 94 public ConsoleSpammingMockitoJUnitRunner(Class<?> klass) throws InvocationTargetException { 95 this(new ConsoleMockitoLogger(), new RunnerFactory().create(klass)); 96 } 97 98 ConsoleSpammingMockitoJUnitRunner(MockitoLogger logger, RunnerImpl runnerImpl) { 99 this.runner = runnerImpl; 100 this.logger = logger; 101 } 102 103 @Override 104 public void run(RunNotifier notifier) { 105 RunListener listener = new RunListener() { 106 WarningsCollector warningsCollector; 107 108 @Override 109 public void testStarted(Description description) throws Exception { 110 warningsCollector = new WarningsCollector(); 111 } 112 113 @Override public void testFailure(Failure failure) throws Exception { 114 logger.log(warningsCollector.getWarnings()); 115 } 116 }; 117 118 notifier.addListener(listener); 119 120 runner.run(notifier); 121 } 122 123 @Override 124 public Description getDescription() { 125 return runner.getDescription(); 126 } 127 128 public void filter(Filter filter) throws NoTestsRemainException { 129 //filter is required because without it UnrootedTests show up in Eclipse 130 runner.filter(filter); 131 } 132}