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