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