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}