2015-01-06 47 views
1

我有一个向REST API发出请求并获得响应的类。我想为此做一个测试。通常情况下,我应该这样的测试:如何更改Mocked对象的字段值

A a = new A("url"); 
a.login("username", "password"); 
List<String> expected = new ArrayList<>(); 

B b = new B(a); 
expected.add("result1"); 
assertEqual(b.request(...), expected); 

这不会起作用,因为我必须登录API和结果并不总是相同的。所以我就用这个测试:

//server that emulate the api 
server.response(Response.from("response.json")); 

A a = mock(A.class); 
when(a.isLoggedIn()).thenReturn(true); 

B b = new B(a); 

与本次测试的问题是我必须在URL设置为,否则将无法正常工作。 A没有吸气剂,我不能改变它。我该如何解决这个问题?

+0

不类'A'有一个'getURL()',它将被'B'调用?如果是的话,只需添加'when(a.getURL())。thenReturn(a_url);' – bachr

+0

不,它不! – Hunsu

+1

你应该添加getter方法,有没有理由不能/不会? – bachr

回答

1

你总是可以接触到反射,周围有很多助手。 ReflectionUtils

在这种情况下,像下面会做

Field field = ReflectionUtils.findField(a.getClass(), [Field name], [Field type]); 
    ReflectionUtils.makeAccessible(field); 
    ReflectionUtils.setField(field, a, [Field value]); 

类可以独立使用,这样你就不必使用弹簧,如果你不是已经

/** 
* Simple utility class for working with the reflection API and handling 
* reflection exceptions. 
* 
* <p>Only intended for internal use. 
* 
* @author Juergen Hoeller 
* @author Rob Harrop 
* @author Rod Johnson 
* @author Costin Leau 
* @author Sam Brannen 
* @author Chris Beams 
* @since 1.2.2 
*/ 
public abstract class ReflectionUtils { 

    /** 
    * Pattern for detecting CGLIB-renamed methods. 
    * @see #isCglibRenamedMethod 
    */ 
    private static final Pattern CGLIB_RENAMED_METHOD_PATTERN = Pattern.compile("CGLIB\\$(.+)\\$\\d+"); 


    /** 
    * Attempt to find a {@link Field field} on the supplied {@link Class} with the 
    * supplied {@code name}. Searches all superclasses up to {@link Object}. 
    * @param clazz the class to introspect 
    * @param name the name of the field 
    * @return the corresponding Field object, or {@code null} if not found 
    */ 
    public static Field findField(Class<?> clazz, String name) { 
     return findField(clazz, name, null); 
    } 

    /** 
    * Attempt to find a {@link Field field} on the supplied {@link Class} with the 
    * supplied {@code name} and/or {@link Class type}. Searches all superclasses 
    * up to {@link Object}. 
    * @param clazz the class to introspect 
    * @param name the name of the field (may be {@code null} if type is specified) 
    * @param type the type of the field (may be {@code null} if name is specified) 
    * @return the corresponding Field object, or {@code null} if not found 
    */ 
    public static Field findField(Class<?> clazz, String name, Class<?> type) { 
     Class<?> searchType = clazz; 
     while (!Object.class.equals(searchType) && searchType != null) { 
      Field[] fields = searchType.getDeclaredFields(); 
      for (Field field : fields) { 
       if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { 
        return field; 
       } 
      } 
      searchType = searchType.getSuperclass(); 
     } 
     return null; 
    } 

    /** 
    * Set the field represented by the supplied {@link Field field object} on the 
    * specified {@link Object target object} to the specified {@code value}. 
    * In accordance with {@link Field#set(Object, Object)} semantics, the new value 
    * is automatically unwrapped if the underlying field has a primitive type. 
    * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. 
    * @param field the field to set 
    * @param target the target object on which to set the field 
    * @param value the value to set; may be {@code null} 
    */ 
    public static void setField(Field field, Object target, Object value) { 
     try { 
      field.set(target, value); 
     } 
     catch (IllegalAccessException ex) { 
      handleReflectionException(ex); 
      throw new IllegalStateException(
        "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); 
     } 
    } 

    /** 
    * Get the field represented by the supplied {@link Field field object} on the 
    * specified {@link Object target object}. In accordance with {@link Field#get(Object)} 
    * semantics, the returned value is automatically wrapped if the underlying field 
    * has a primitive type. 
    * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. 
    * @param field the field to get 
    * @param target the target object from which to get the field 
    * @return the field's current value 
    */ 
    public static Object getField(Field field, Object target) { 
     try { 
      return field.get(target); 
     } 
     catch (IllegalAccessException ex) { 
      handleReflectionException(ex); 
      throw new IllegalStateException(
        "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage()); 
     } 
    } 

    /** 
    * Attempt to find a {@link Method} on the supplied class with the supplied name 
    * and no parameters. Searches all superclasses up to {@code Object}. 
    * <p>Returns {@code null} if no {@link Method} can be found. 
    * @param clazz the class to introspect 
    * @param name the name of the method 
    * @return the Method object, or {@code null} if none found 
    */ 
    public static Method findMethod(Class<?> clazz, String name) { 
     return findMethod(clazz, name, new Class<?>[0]); 
    } 

    /** 
    * Attempt to find a {@link Method} on the supplied class with the supplied name 
    * and parameter types. Searches all superclasses up to {@code Object}. 
    * <p>Returns {@code null} if no {@link Method} can be found. 
    * @param clazz the class to introspect 
    * @param name the name of the method 
    * @param paramTypes the parameter types of the method 
    * (may be {@code null} to indicate any signature) 
    * @return the Method object, or {@code null} if none found 
    */ 
    public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { 
     Class<?> searchType = clazz; 
     while (searchType != null) { 
      Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods()); 
      for (Method method : methods) { 
       if (name.equals(method.getName()) && 
         (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { 
        return method; 
       } 
      } 
      searchType = searchType.getSuperclass(); 
     } 
     return null; 
    } 

    /** 
    * Invoke the specified {@link Method} against the supplied target object with no arguments. 
    * The target object can be {@code null} when invoking a static {@link Method}. 
    * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}. 
    * @param method the method to invoke 
    * @param target the target object to invoke the method on 
    * @return the invocation result, if any 
    * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) 
    */ 
    public static Object invokeMethod(Method method, Object target) { 
     return invokeMethod(method, target, new Object[0]); 
    } 

    /** 
    * Invoke the specified {@link Method} against the supplied target object with the 
    * supplied arguments. The target object can be {@code null} when invoking a 
    * static {@link Method}. 
    * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}. 
    * @param method the method to invoke 
    * @param target the target object to invoke the method on 
    * @param args the invocation arguments (may be {@code null}) 
    * @return the invocation result, if any 
    */ 
    public static Object invokeMethod(Method method, Object target, Object... args) { 
     try { 
      return method.invoke(target, args); 
     } 
     catch (Exception ex) { 
      handleReflectionException(ex); 
     } 
     throw new IllegalStateException("Should never get here"); 
    } 

    /** 
    * Invoke the specified JDBC API {@link Method} against the supplied target 
    * object with no arguments. 
    * @param method the method to invoke 
    * @param target the target object to invoke the method on 
    * @return the invocation result, if any 
    * @throws SQLException the JDBC API SQLException to rethrow (if any) 
    * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[]) 
    */ 
    public static Object invokeJdbcMethod(Method method, Object target) throws SQLException { 
     return invokeJdbcMethod(method, target, new Object[0]); 
    } 

    /** 
    * Invoke the specified JDBC API {@link Method} against the supplied target 
    * object with the supplied arguments. 
    * @param method the method to invoke 
    * @param target the target object to invoke the method on 
    * @param args the invocation arguments (may be {@code null}) 
    * @return the invocation result, if any 
    * @throws SQLException the JDBC API SQLException to rethrow (if any) 
    * @see #invokeMethod(java.lang.reflect.Method, Object, Object[]) 
    */ 
    public static Object invokeJdbcMethod(Method method, Object target, Object... args) throws SQLException { 
     try { 
      return method.invoke(target, args); 
     } 
     catch (IllegalAccessException ex) { 
      handleReflectionException(ex); 
     } 
     catch (InvocationTargetException ex) { 
      if (ex.getTargetException() instanceof SQLException) { 
       throw (SQLException) ex.getTargetException(); 
      } 
      handleInvocationTargetException(ex); 
     } 
     throw new IllegalStateException("Should never get here"); 
    } 

    /** 
    * Handle the given reflection exception. Should only be called if no 
    * checked exception is expected to be thrown by the target method. 
    * <p>Throws the underlying RuntimeException or Error in case of an 
    * InvocationTargetException with such a root cause. Throws an 
    * IllegalStateException with an appropriate message else. 
    * @param ex the reflection exception to handle 
    */ 
    public static void handleReflectionException(Exception ex) { 
     if (ex instanceof NoSuchMethodException) { 
      throw new IllegalStateException("Method not found: " + ex.getMessage()); 
     } 
     if (ex instanceof IllegalAccessException) { 
      throw new IllegalStateException("Could not access method: " + ex.getMessage()); 
     } 
     if (ex instanceof InvocationTargetException) { 
      handleInvocationTargetException((InvocationTargetException) ex); 
     } 
     if (ex instanceof RuntimeException) { 
      throw (RuntimeException) ex; 
     } 
     throw new UndeclaredThrowableException(ex); 
    } 

    /** 
    * Handle the given invocation target exception. Should only be called if no 
    * checked exception is expected to be thrown by the target method. 
    * <p>Throws the underlying RuntimeException or Error in case of such a root 
    * cause. Throws an IllegalStateException else. 
    * @param ex the invocation target exception to handle 
    */ 
    public static void handleInvocationTargetException(InvocationTargetException ex) { 
     rethrowRuntimeException(ex.getTargetException()); 
    } 

    /** 
    * Rethrow the given {@link Throwable exception}, which is presumably the 
    * <em>target exception</em> of an {@link InvocationTargetException}. Should 
    * only be called if no checked exception is expected to be thrown by the 
    * target method. 
    * <p>Rethrows the underlying exception cast to an {@link RuntimeException} or 
    * {@link Error} if appropriate; otherwise, throws an 
    * {@link IllegalStateException}. 
    * @param ex the exception to rethrow 
    * @throws RuntimeException the rethrown exception 
    */ 
    public static void rethrowRuntimeException(Throwable ex) { 
     if (ex instanceof RuntimeException) { 
      throw (RuntimeException) ex; 
     } 
     if (ex instanceof Error) { 
      throw (Error) ex; 
     } 
     throw new UndeclaredThrowableException(ex); 
    } 

    /** 
    * Rethrow the given {@link Throwable exception}, which is presumably the 
    * <em>target exception</em> of an {@link InvocationTargetException}. Should 
    * only be called if no checked exception is expected to be thrown by the 
    * target method. 
    * <p>Rethrows the underlying exception cast to an {@link Exception} or 
    * {@link Error} if appropriate; otherwise, throws an 
    * {@link IllegalStateException}. 
    * @param ex the exception to rethrow 
    * @throws Exception the rethrown exception (in case of a checked exception) 
    */ 
    public static void rethrowException(Throwable ex) throws Exception { 
     if (ex instanceof Exception) { 
      throw (Exception) ex; 
     } 
     if (ex instanceof Error) { 
      throw (Error) ex; 
     } 
     throw new UndeclaredThrowableException(ex); 
    } 

    /** 
    * Determine whether the given method explicitly declares the given 
    * exception or one of its superclasses, which means that an exception of 
    * that type can be propagated as-is within a reflective invocation. 
    * @param method the declaring method 
    * @param exceptionType the exception to throw 
    * @return {@code true} if the exception can be thrown as-is; 
    * {@code false} if it needs to be wrapped 
    */ 
    public static boolean declaresException(Method method, Class<?> exceptionType) { 
     Class<?>[] declaredExceptions = method.getExceptionTypes(); 
     for (Class<?> declaredException : declaredExceptions) { 
      if (declaredException.isAssignableFrom(exceptionType)) { 
       return true; 
      } 
     } 
     return false; 
    } 

    /** 
    * Determine whether the given field is a "public static final" constant. 
    * @param field the field to check 
    */ 
    public static boolean isPublicStaticFinal(Field field) { 
     int modifiers = field.getModifiers(); 
     return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)); 
    } 

    /** 
    * Determine whether the given method is an "equals" method. 
    * @see java.lang.Object#equals(Object) 
    */ 
    public static boolean isEqualsMethod(Method method) { 
     if (method == null || !method.getName().equals("equals")) { 
      return false; 
     } 
     Class<?>[] paramTypes = method.getParameterTypes(); 
     return (paramTypes.length == 1 && paramTypes[0] == Object.class); 
    } 

    /** 
    * Determine whether the given method is a "hashCode" method. 
    * @see java.lang.Object#hashCode() 
    */ 
    public static boolean isHashCodeMethod(Method method) { 
     return (method != null && method.getName().equals("hashCode") && method.getParameterTypes().length == 0); 
    } 

    /** 
    * Determine whether the given method is a "toString" method. 
    * @see java.lang.Object#toString() 
    */ 
    public static boolean isToStringMethod(Method method) { 
     return (method != null && method.getName().equals("toString") && method.getParameterTypes().length == 0); 
    } 

    /** 
    * Determine whether the given method is originally declared by {@link java.lang.Object}. 
    */ 
    public static boolean isObjectMethod(Method method) { 
     if (method == null) { 
      return false; 
     } 
     try { 
      Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes()); 
      return true; 
     } 
     catch (Exception ex) { 
      return false; 
     } 
    } 

    /** 
    * Determine whether the given method is a CGLIB 'renamed' method, 
    * following the pattern "CGLIB$methodName$0". 
    * @param renamedMethod the method to check 
    * @see org.springframework.cglib.proxy.Enhancer#rename 
    */ 
    public static boolean isCglibRenamedMethod(Method renamedMethod) { 
     return CGLIB_RENAMED_METHOD_PATTERN.matcher(renamedMethod.getName()).matches(); 
    } 

    /** 
    * Make the given field accessible, explicitly setting it accessible if 
    * necessary. The {@code setAccessible(true)} method is only called 
    * when actually necessary, to avoid unnecessary conflicts with a JVM 
    * SecurityManager (if active). 
    * @param field the field to make accessible 
    * @see java.lang.reflect.Field#setAccessible 
    */ 
    public static void makeAccessible(Field field) { 
     if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || 
       Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { 
      field.setAccessible(true); 
     } 
    } 

    /** 
    * Make the given method accessible, explicitly setting it accessible if 
    * necessary. The {@code setAccessible(true)} method is only called 
    * when actually necessary, to avoid unnecessary conflicts with a JVM 
    * SecurityManager (if active). 
    * @param method the method to make accessible 
    * @see java.lang.reflect.Method#setAccessible 
    */ 
    public static void makeAccessible(Method method) { 
     if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) 
       && !method.isAccessible()) { 
      method.setAccessible(true); 
     } 
    } 

    /** 
    * Make the given constructor accessible, explicitly setting it accessible 
    * if necessary. The {@code setAccessible(true)} method is only called 
    * when actually necessary, to avoid unnecessary conflicts with a JVM 
    * SecurityManager (if active). 
    * @param ctor the constructor to make accessible 
    * @see java.lang.reflect.Constructor#setAccessible 
    */ 
    public static void makeAccessible(Constructor<?> ctor) { 
     if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) 
       && !ctor.isAccessible()) { 
      ctor.setAccessible(true); 
     } 
    } 

    /** 
    * Perform the given callback operation on all matching methods of the given 
    * class and superclasses. 
    * <p>The same named method occurring on subclass and superclass will appear 
    * twice, unless excluded by a {@link MethodFilter}. 
    * @param clazz class to start looking at 
    * @param mc the callback to invoke for each method 
    * @see #doWithMethods(Class, MethodCallback, MethodFilter) 
    */ 
    public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException { 
     doWithMethods(clazz, mc, null); 
    } 

    /** 
    * Perform the given callback operation on all matching methods of the given 
    * class and superclasses (or given interface and super-interfaces). 
    * <p>The same named method occurring on subclass and superclass will appear 
    * twice, unless excluded by the specified {@link MethodFilter}. 
    * @param clazz class to start looking at 
    * @param mc the callback to invoke for each method 
    * @param mf the filter that determines the methods to apply the callback to 
    */ 
    public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) 
      throws IllegalArgumentException { 

     // Keep backing up the inheritance hierarchy. 
     Method[] methods = clazz.getDeclaredMethods(); 
     for (Method method : methods) { 
      if (mf != null && !mf.matches(method)) { 
       continue; 
      } 
      try { 
       mc.doWith(method); 
      } 
      catch (IllegalAccessException ex) { 
       throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() 
         + "': " + ex); 
      } 
     } 
     if (clazz.getSuperclass() != null) { 
      doWithMethods(clazz.getSuperclass(), mc, mf); 
     } 
     else if (clazz.isInterface()) { 
      for (Class<?> superIfc : clazz.getInterfaces()) { 
       doWithMethods(superIfc, mc, mf); 
      } 
     } 
    } 

    /** 
    * Get all declared methods on the leaf class and all superclasses. Leaf 
    * class methods are included first. 
    */ 
    public static Method[] getAllDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException { 
     final List<Method> methods = new ArrayList<Method>(32); 
     doWithMethods(leafClass, new MethodCallback() { 
      public void doWith(Method method) { 
       methods.add(method); 
      } 
     }); 
     return methods.toArray(new Method[methods.size()]); 
    } 

    /** 
    * Get the unique set of declared methods on the leaf class and all superclasses. Leaf 
    * class methods are included first and while traversing the superclass hierarchy any methods found 
    * with signatures matching a method already included are filtered out. 
    */ 
    public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException { 
     final List<Method> methods = new ArrayList<Method>(32); 
     doWithMethods(leafClass, new MethodCallback() { 
      public void doWith(Method method) { 
       boolean knownSignature = false; 
       Method methodBeingOverriddenWithCovariantReturnType = null; 
       for (Method existingMethod : methods) { 
        if (method.getName().equals(existingMethod.getName()) && 
          Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) { 
         // Is this a covariant return type situation? 
         if (existingMethod.getReturnType() != method.getReturnType() && 
           existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) { 
          methodBeingOverriddenWithCovariantReturnType = existingMethod; 
         } 
         else { 
          knownSignature = true; 
         } 
         break; 
        } 
       } 
       if (methodBeingOverriddenWithCovariantReturnType != null) { 
        methods.remove(methodBeingOverriddenWithCovariantReturnType); 
       } 
       if (!knownSignature && !isCglibRenamedMethod(method)) { 
        methods.add(method); 
       } 
      } 
     }); 
     return methods.toArray(new Method[methods.size()]); 
    } 

    /** 
    * Invoke the given callback on all fields in the target class, going up the 
    * class hierarchy to get all declared fields. 
    * @param clazz the target class to analyze 
    * @param fc the callback to invoke for each field 
    */ 
    public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException { 
     doWithFields(clazz, fc, null); 
    } 

    /** 
    * Invoke the given callback on all fields in the target class, going up the 
    * class hierarchy to get all declared fields. 
    * @param clazz the target class to analyze 
    * @param fc the callback to invoke for each field 
    * @param ff the filter that determines the fields to apply the callback to 
    */ 
    public static void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff) 
      throws IllegalArgumentException { 

     // Keep backing up the inheritance hierarchy. 
     Class<?> targetClass = clazz; 
     do { 
      Field[] fields = targetClass.getDeclaredFields(); 
      for (Field field : fields) { 
       // Skip static and final fields. 
       if (ff != null && !ff.matches(field)) { 
        continue; 
       } 
       try { 
        fc.doWith(field); 
       } 
       catch (IllegalAccessException ex) { 
        throw new IllegalStateException(
          "Shouldn't be illegal to access field '" + field.getName() + "': " + ex); 
       } 
      } 
      targetClass = targetClass.getSuperclass(); 
     } 
     while (targetClass != null && targetClass != Object.class); 
    } 

    /** 
    * Given the source object and the destination, which must be the same class 
    * or a subclass, copy all fields, including inherited fields. Designed to 
    * work on objects with public no-arg constructors. 
    * @throws IllegalArgumentException if the arguments are incompatible 
    */ 
    public static void shallowCopyFieldState(final Object src, final Object dest) throws IllegalArgumentException { 
     if (src == null) { 
      throw new IllegalArgumentException("Source for field copy cannot be null"); 
     } 
     if (dest == null) { 
      throw new IllegalArgumentException("Destination for field copy cannot be null"); 
     } 
     if (!src.getClass().isAssignableFrom(dest.getClass())) { 
      throw new IllegalArgumentException("Destination class [" + dest.getClass().getName() 
        + "] must be same or subclass as source class [" + src.getClass().getName() + "]"); 
     } 
     doWithFields(src.getClass(), new FieldCallback() { 
      public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { 
       makeAccessible(field); 
       Object srcValue = field.get(src); 
       field.set(dest, srcValue); 
      } 
     }, COPYABLE_FIELDS); 
    } 


    /** 
    * Action to take on each method. 
    */ 
    public interface MethodCallback { 

     /** 
     * Perform an operation using the given method. 
     * @param method the method to operate on 
     */ 
     void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; 
    } 


    /** 
    * Callback optionally used to filter methods to be operated on by a method callback. 
    */ 
    public interface MethodFilter { 

     /** 
     * Determine whether the given method matches. 
     * @param method the method to check 
     */ 
     boolean matches(Method method); 
    } 


    /** 
    * Callback interface invoked on each field in the hierarchy. 
    */ 
    public interface FieldCallback { 

     /** 
     * Perform an operation using the given field. 
     * @param field the field to operate on 
     */ 
     void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; 
    } 


    /** 
    * Callback optionally used to filter fields to be operated on by a field callback. 
    */ 
    public interface FieldFilter { 

     /** 
     * Determine whether the given field matches. 
     * @param field the field to check 
     */ 
     boolean matches(Field field); 
    } 


    /** 
    * Pre-built FieldFilter that matches all non-static, non-final fields. 
    */ 
    public static FieldFilter COPYABLE_FIELDS = new FieldFilter() { 

     public boolean matches(Field field) { 
      return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())); 
     } 
    }; 


    /** 
    * Pre-built MethodFilter that matches all non-bridge methods. 
    */ 
    public static MethodFilter NON_BRIDGED_METHODS = new MethodFilter() { 

     public boolean matches(Method method) { 
      return !method.isBridge(); 
     } 
    }; 


    /** 
    * Pre-built MethodFilter that matches all non-bridge methods 
    * which are not declared on {@code java.lang.Object}. 
    */ 
    public static MethodFilter USER_DECLARED_METHODS = new MethodFilter() { 

     public boolean matches(Method method) { 
      return (!method.isBridge() && method.getDeclaringClass() != Object.class); 
     } 
    }; 

} 
+1

这可能不是一个好方法,如果将来您特别是其他人)必须做一些重新分解。 – bachr

+0

是真的,非常清楚这一点,但所有的方法都有其不足之处,就像测试私有方法http://java.dzone.com/articles/unit-testing-private-methods的两难处境,信息性的决定 –

+0

同意,所有的方法都有其自身的局限性。 – bachr

0

您应该提供类似getURL()的方法访问类A中的网址。然后从B使用此方法。 嘲笑A只需加上when(a.getURL()).thenReturn(a_url);