• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.junit;
6 
7 import org.mockito.internal.invocation.finder.AllInvocationsFinder;
8 import org.mockito.internal.stubbing.UnusedStubbingReporting;
9 import org.mockito.internal.util.collections.ListUtil.Filter;
10 import org.mockito.invocation.Invocation;
11 import org.mockito.stubbing.Stubbing;
12 
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.LinkedHashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 
20 import static org.mockito.internal.util.collections.ListUtil.filter;
21 
22 /**
23  * Finds unused stubbings
24  */
25 public class UnusedStubbingsFinder {
26 
27     /**
28      * Gets all unused stubbings for given set of mock objects, in order.
29      * Stubbings explicitily marked as LENIENT are not included.
30      */
getUnusedStubbings(Iterable<Object> mocks)31     public UnusedStubbings getUnusedStubbings(Iterable<Object> mocks) {
32         Set<Stubbing> stubbings = AllInvocationsFinder.findStubbings(mocks);
33 
34         List<Stubbing> unused = filter(stubbings, new Filter<Stubbing>() {
35             public boolean isOut(Stubbing s) {
36                 return !UnusedStubbingReporting.shouldBeReported(s);
37             }
38         });
39 
40         return new UnusedStubbings(unused);
41     }
42 
43     /**
44      * Gets unused stubbings per location. This method is less accurate than {@link #getUnusedStubbings(Iterable)}.
45      * It considers that stubbings with the same location (e.g. ClassFile + line number) are the same.
46      * This is not completely accurate because a stubbing declared in a setup or constructor
47      * is created per each test method. Because those are different test methods,
48      * different mocks are created, different 'Invocation' instance is backing the 'Stubbing' instance.
49      * In certain scenarios (detecting unused stubbings by JUnit runner), we need this exact level of accuracy.
50      * Stubbing declared in constructor but realized in % of test methods is considered as 'used' stubbing.
51      * There are high level unit tests that demonstrate this scenario.
52      */
getUnusedStubbingsByLocation(Iterable<Object> mocks)53     public Collection<Invocation> getUnusedStubbingsByLocation(Iterable<Object> mocks) {
54         Set<Stubbing> stubbings = AllInvocationsFinder.findStubbings(mocks);
55 
56         //1st pass, collect all the locations of the stubbings that were used
57         //note that those are _not_ locations where the stubbings was used
58         Set<String> locationsOfUsedStubbings = new HashSet<String>();
59         for (Stubbing s : stubbings) {
60             if (!UnusedStubbingReporting.shouldBeReported(s)) {
61                 String location = s.getInvocation().getLocation().toString();
62                 locationsOfUsedStubbings.add(location);
63             }
64         }
65 
66         //2nd pass, collect unused stubbings by location
67         //If the location matches we assume the stubbing was used in at least one test method
68         //Also, using map to deduplicate reported unused stubbings
69         // if unused stubbing appear in the setup method / constructor we don't want to report it per each test case
70         Map<String, Invocation> out = new LinkedHashMap<String, Invocation>();
71         for (Stubbing s : stubbings) {
72             String location = s.getInvocation().getLocation().toString();
73             if (!locationsOfUsedStubbings.contains(location)) {
74                 out.put(location, s.getInvocation());
75             }
76         }
77 
78         return out.values();
79     }
80 }
81