• 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 static org.mockito.internal.util.collections.ListUtil.filter;
8 
9 import java.util.Collection;
10 import java.util.HashSet;
11 import java.util.LinkedHashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 
16 import org.mockito.internal.invocation.finder.AllInvocationsFinder;
17 import org.mockito.internal.stubbing.UnusedStubbingReporting;
18 import org.mockito.internal.util.collections.ListUtil.Filter;
19 import org.mockito.invocation.Invocation;
20 import org.mockito.stubbing.Stubbing;
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 =
35                 filter(
36                         stubbings,
37                         new Filter<Stubbing>() {
38                             @Override
39                             public boolean isOut(Stubbing s) {
40                                 return !UnusedStubbingReporting.shouldBeReported(s);
41                             }
42                         });
43 
44         return new UnusedStubbings(unused);
45     }
46 
47     /**
48      * Gets unused stubbings per location. This method is less accurate than {@link #getUnusedStubbings(Iterable)}.
49      * It considers that stubbings with the same location (e.g. ClassFile + line number) are the same.
50      * This is not completely accurate because a stubbing declared in a setup or constructor
51      * is created per each test method. Because those are different test methods,
52      * different mocks are created, different 'Invocation' instance is backing the 'Stubbing' instance.
53      * In certain scenarios (detecting unused stubbings by JUnit runner), we need this exact level of accuracy.
54      * Stubbing declared in constructor but realized in % of test methods is considered as 'used' stubbing.
55      * There are high level unit tests that demonstrate this scenario.
56      */
getUnusedStubbingsByLocation(Iterable<Object> mocks)57     public Collection<Invocation> getUnusedStubbingsByLocation(Iterable<Object> mocks) {
58         Set<Stubbing> stubbings = AllInvocationsFinder.findStubbings(mocks);
59 
60         // 1st pass, collect all the locations of the stubbings that were used
61         // note that those are _not_ locations where the stubbings was used
62         Set<String> locationsOfUsedStubbings = new HashSet<>();
63         for (Stubbing s : stubbings) {
64             if (!UnusedStubbingReporting.shouldBeReported(s)) {
65                 String location = s.getInvocation().getLocation().toString();
66                 locationsOfUsedStubbings.add(location);
67             }
68         }
69 
70         // 2nd pass, collect unused stubbings by location
71         // If the location matches we assume the stubbing was used in at least one test method
72         // Also, using map to deduplicate reported unused stubbings
73         // if unused stubbing appear in the setup method / constructor we don't want to report it
74         // per each test case
75         Map<String, Invocation> out = new LinkedHashMap<>();
76         for (Stubbing s : stubbings) {
77             String location = s.getInvocation().getLocation().toString();
78             if (!locationsOfUsedStubbings.contains(location)) {
79                 out.put(location, s.getInvocation());
80             }
81         }
82 
83         return out.values();
84     }
85 }
86