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