Repository: mockito/mockito
Issue: ArgumentCaptor: NPE when an null array is passed to a varargs method
Labels: bug

A NullPointerException is thrown if an null array is passed to a varargs method.

```
@Test
public void shouldCaptureVarArgs_nullArrayArg() {
        String[] argArray = null;
        mock.varargs(argArray);

        verify(mock).varargs(captor.capture()); //<- Kaboom NPE
}
```

Since an vararg is simply an array the ArgumentCaptor should reject it silently. This relates to #565.

```
java.lang.NullPointerException
    at java.lang.reflect.Array.getLength(Native Method)
    at org.mockito.internal.invocation.InvocationMatcher.captureVarargsPart(InvocationMatcher.java:142)
    at org.mockito.internal.invocation.InvocationMatcher.captureArgumentsFrom(InvocationMatcher.java:122)
    at org.mockito.internal.invocation.InvocationMarker.markVerified(InvocationMarker.java:24)
    at org.mockito.internal.invocation.InvocationMarker.markVerified(InvocationMarker.java:18)
    at org.mockito.internal.verification.checkers.NumberOfInvocationsChecker.check(NumberOfInvocationsChecker.java:43)
    at org.mockito.internal.verification.Times.verify(Times.java:40)
    at org.mockito.internal.verification.MockAwareVerificationMode.verify(MockAwareVerificationMode.java:21)
    at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:73)
    at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:32)
    at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:37)
    at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:36)
    at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.access$0(MockMethodInterceptor.java:32)
    at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:113)
    at org.mockitousage.IMethods$MockitoMock$81506210.varargs(Unknown Source)
    at org.mockitousage.matchers.VarargsTest.shouldCaptureVarArgs_nullArrayArg(VarargsTest.java:182)
```


Issue comments:
Probably related to #567


Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/invocation/ArgumentMatcherAction.java b/src/main/java/org/mockito/internal/invocation/ArgumentMatcherAction.java
new file mode 100644
index 0000000000..29156d0b4c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/invocation/ArgumentMatcherAction.java
@@ -0,0 +1,28 @@
+package org.mockito.internal.invocation;
+
+import org.mockito.ArgumentMatcher;
+
+public interface ArgumentMatcherAction {
+    /**
+     * Implementations must apply the given matcher to the argument and return
+     * <code>true</code> if the operation was successful or <code>false</code>
+     * if not. In this case no more matchers and arguments will be passed by
+     * {@link MatcherApplicationStrategy#forEachMatcherAndArgument(ArgumentMatcherAction)} to this method.
+     * .
+     * 
+     * @param matcher
+     *            to process the argument, never <code>null</code>
+     * @param argument
+     *            to be processed by the matcher, can be <code>null</code>
+     * @return
+     *         <ul>
+     *         <li><code>true</code> if the <b>matcher</b> was successfully
+     *         applied to the <b>argument</b> and the next pair of matcher and
+     *         argument should be passed
+     *         <li><code>false</code> otherwise
+     *         </ul>
+     * 
+     * 
+     */
+    boolean apply(ArgumentMatcher<?> matcher, Object argument);
+}
diff --git a/src/main/java/org/mockito/internal/invocation/ArgumentsComparator.java b/src/main/java/org/mockito/internal/invocation/ArgumentsComparator.java
deleted file mode 100644
index 7b12fb2aff..0000000000
--- a/src/main/java/org/mockito/internal/invocation/ArgumentsComparator.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.invocation;
-
-import java.lang.reflect.Method;
-import java.util.List;
-import org.mockito.ArgumentMatcher;
-import org.mockito.internal.matchers.VarargMatcher;
-import org.mockito.invocation.Invocation;
-
-@SuppressWarnings("unchecked")
-public class ArgumentsComparator {
-
-    private ArgumentsComparator() {}
-
-    public static boolean argumentsMatch(InvocationMatcher invocationMatcher, Invocation actual) {
-        Object[] actualArgs = actual.getArguments();
-        return argumentsMatch(invocationMatcher, actualArgs) || varArgsMatch(invocationMatcher, actual);
-    }
-
-    public static boolean argumentsMatch(InvocationMatcher invocationMatcher, Object[] actualArgs) {
-        if (actualArgs.length != invocationMatcher.getMatchers().size()) {
-            return false;
-        }
-        for (int i = 0; i < actualArgs.length; i++) {
-            ArgumentMatcher<Object> argumentMatcher = invocationMatcher.getMatchers().get(i);
-            Object argument = actualArgs[i];
-
-            if (!matches(argumentMatcher, argument)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    // ok, this method is a little bit messy but the vararg business unfortunately is messy...
-    private static boolean varArgsMatch(InvocationMatcher invocationMatcher, Invocation actual) {
-        if (!actual.getMethod().isVarArgs()) {
-            // if the method is not vararg forget about it
-            return false;
-        }
-
-        // we must use raw arguments, not arguments...
-        Object[] rawArgs = actual.getRawArguments();
-        List<ArgumentMatcher> matchers = invocationMatcher.getMatchers();
-
-        if (rawArgs.length != matchers.size()) {
-            return false;
-        }
-
-        for (int i = 0; i < rawArgs.length; i++) {
-            ArgumentMatcher m = matchers.get(i);
-            // it's a vararg because it's the last array in the arg list
-            if (rawArgs[i] != null && rawArgs[i].getClass().isArray() && i == rawArgs.length - 1) {
-                // this is very important to only allow VarargMatchers here. If
-                // you're not sure why remove it and run all tests.
-                if (!(m instanceof VarargMatcher) || !m.matches(rawArgs[i])) {
-                    return false;
-                }
-                // it's not a vararg (i.e. some ordinary argument before
-                // varargs), just do the ordinary check
-            } else if (!m.matches(rawArgs[i])) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    private static boolean matches(ArgumentMatcher<Object> argumentMatcher, Object argument) {
-        return isCompatible(argumentMatcher, argument) && argumentMatcher.matches(argument);
-    }
-
-    /**
-     * Returns <code>true</code> if the given <b>argument</b> can be passed to
-     * the given <code>argumentMatcher</code> without causing a
-     * {@link ClassCastException}.
-     */
-    private static boolean isCompatible(ArgumentMatcher<?> argumentMatcher, Object argument) {
-        if (argument == null)
-            return true;
-
-        Class<?> expectedArgumentType = getArgumentType(argumentMatcher);
-
-        return expectedArgumentType.isInstance(argument);
-    }
-
-    /**
-     * Returns the type of {@link ArgumentMatcher#matches(Object)} of the given
-     * {@link ArgumentMatcher} implementation.
-     */
-    private static Class<?> getArgumentType(ArgumentMatcher<?> argumentMatcher) {
-        Method[] methods = argumentMatcher.getClass().getMethods();
-        for (Method method : methods) {
-            if (isMatchesMethod(method)) {
-                return method.getParameterTypes()[0];
-            }
-        }
-        throw new NoSuchMethodError("Method 'matches(T)' not found in ArgumentMatcher: " + argumentMatcher + " !\r\n Please file a bug with this stack trace at: https://github.com/mockito/mockito/issues/new ");
-    }
-
-    /**
-     * Returns <code>true</code> if the given method is
-     * {@link ArgumentMatcher#matches(Object)}
-     */
-    private static boolean isMatchesMethod(Method method) {
-        if (method.getParameterTypes().length != 1) {
-            return false;
-        }
-        if (method.isBridge()) {
-            return false;
-        }
-        return method.getName().equals("matches");
-    }
-}
diff --git a/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java b/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java
index 66ea8f5fbf..d955b70013 100644
--- a/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java
+++ b/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java
@@ -5,6 +5,15 @@
 
 package org.mockito.internal.invocation;
 
+import static org.mockito.internal.invocation.ArgumentsProcessor.argumentsToMatchers;
+import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor;
+import static org.mockito.internal.invocation.TypeSafeMatching.matchesTypeSafe;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
 import org.mockito.ArgumentMatcher;
 import org.mockito.internal.matchers.CapturesArguments;
 import org.mockito.internal.reporting.PrintSettings;
@@ -12,36 +21,36 @@
 import org.mockito.invocation.Invocation;
 import org.mockito.invocation.Location;
 
-import static org.mockito.internal.invocation.ArgumentsComparator.argumentsMatch;
-
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-import java.util.*;
-
-@SuppressWarnings("unchecked")
 /**
- * In addition to all content of the invocation, the invocation matcher contains the argument matchers.
- * Invocation matcher is used during verification and stubbing.
- * In those cases, the user can provide argument matchers instead of 'raw' arguments.
- * Raw arguments are converted to 'equals' matchers anyway.
+ * In addition to all content of the invocation, the invocation matcher contains the argument matchers. Invocation matcher is used during verification and stubbing. In those cases, the user can provide argument matchers instead of 'raw' arguments. Raw arguments are converted to 'equals' matchers anyway.
  */
+@SuppressWarnings("serial")
 public class InvocationMatcher implements DescribedInvocation, CapturesArgumentsFromInvocation, Serializable {
 
     private final Invocation invocation;
-    private final List<ArgumentMatcher> matchers;
+    private final List<ArgumentMatcher<?>> matchers;
 
+    @SuppressWarnings({ "rawtypes", "unchecked" })
     public InvocationMatcher(Invocation invocation, List<ArgumentMatcher> matchers) {
         this.invocation = invocation;
         if (matchers.isEmpty()) {
-            this.matchers = ArgumentsProcessor.argumentsToMatchers(invocation.getArguments());
+            this.matchers = (List) argumentsToMatchers(invocation.getArguments());
         } else {
-            this.matchers = matchers;
+            this.matchers = (List) matchers;
         }
     }
 
+    @SuppressWarnings("rawtypes")
     public InvocationMatcher(Invocation invocation) {
-        this(invocation, Collections.<ArgumentMatcher>emptyList());
+        this(invocation, Collections.<ArgumentMatcher> emptyList());
+    }
+
+    public static List<InvocationMatcher> createFrom(List<Invocation> invocations) {
+        LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>();
+        for (Invocation i : invocations) {
+            out.add(new InvocationMatcher(i));
+        }
+        return out;
     }
 
     public Method getMethod() {
@@ -49,54 +58,50 @@ public Method getMethod() {
     }
 
     public Invocation getInvocation() {
-        return this.invocation;
+        return invocation;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     public List<ArgumentMatcher> getMatchers() {
-        return this.matchers;
+        return (List) matchers;
     }
 
+    @Override
+    @SuppressWarnings({ "unchecked", "rawtypes" })
     public String toString() {
-        return new PrintSettings().print(matchers, invocation);
+        return new PrintSettings().print((List) matchers, invocation);
     }
 
     public boolean matches(Invocation actual) {
-        return invocation.getMock().equals(actual.getMock())
-                && hasSameMethod(actual)
-                && argumentsMatch(this, actual);
-    }
-
-    private boolean safelyArgumentsMatch(Object[] actualArgs) {
-        try {
-            return argumentsMatch(this, actualArgs);
-        } catch (Throwable t) {
-            return false;
-        }
+        return invocation.getMock().equals(actual.getMock()) && hasSameMethod(actual) && argumentsMatch(actual);
     }
 
     /**
-     * similar means the same method name, same mock, unverified
-     * and: if arguments are the same cannot be overloaded
+     * similar means the same method name, same mock, unverified and: if arguments are the same cannot be overloaded
      */
     public boolean hasSimilarMethod(Invocation candidate) {
         String wantedMethodName = getMethod().getName();
-        String currentMethodName = candidate.getMethod().getName();
-
-        final boolean methodNameEquals = wantedMethodName.equals(currentMethodName);
-        final boolean isUnverified = !candidate.isVerified();
-        final boolean mockIsTheSame = getInvocation().getMock() == candidate.getMock();
-        final boolean methodEquals = hasSameMethod(candidate);
+        String candidateMethodName = candidate.getMethod().getName();
 
-        if (!methodNameEquals || !isUnverified || !mockIsTheSame) {
+        if (!wantedMethodName.equals(candidateMethodName)) {
             return false;
         }
+        if (candidate.isVerified()) {
+            return false;
+        }
+        if (getInvocation().getMock() != candidate.getMock()) {
+            return false;
+        }
+        if (hasSameMethod(candidate)) {
+            return true;
+        }
 
-        final boolean overloadedButSameArgs = !methodEquals && safelyArgumentsMatch(candidate.getArguments());
-
-        return !overloadedButSameArgs;
+        return !argumentsMatch(candidate);
     }
 
     public boolean hasSameMethod(Invocation candidate) {
+        // not using method.equals() for 1 good reason:
+        // sometimes java generates forwarding methods when generics are in play see JavaGenericsForwardingMethodsTest
         Method m1 = invocation.getMethod();
         Method m2 = candidate.getMethod();
 
@@ -115,59 +120,34 @@ public boolean hasSameMethod(Invocation candidate) {
         return false;
     }
 
+    @Override
     public Location getLocation() {
         return invocation.getLocation();
     }
 
+    @Override
     public void captureArgumentsFrom(Invocation invocation) {
-        captureRegularArguments(invocation);
-        captureVarargsPart(invocation);
+        MatcherApplicationStrategy strategy = getMatcherApplicationStrategyFor(invocation, matchers);
+        strategy.forEachMatcherAndArgument(captureArgument());
     }
 
-    private void captureRegularArguments(Invocation invocation) {
-        for (int position = 0; position < regularArgumentsSize(invocation); position++) {
-            ArgumentMatcher m = matchers.get(position);
-            if (m instanceof CapturesArguments) {
-                ((CapturesArguments) m).captureFrom(invocation.getArgument(position));
-            }
-        }
-    }
-
-    private void captureVarargsPart(Invocation invocation) {
-        if (!invocation.getMethod().isVarArgs()) {
-            return;
-        }
-        int indexOfVararg = invocation.getRawArguments().length - 1;
-        for (ArgumentMatcher m : uniqueMatcherSet(indexOfVararg)) {
-            if (m instanceof CapturesArguments) {
-                Object rawArgument = invocation.getRawArguments()[indexOfVararg];
-                for (int i = 0; i < Array.getLength(rawArgument); i++) {
-                    ((CapturesArguments) m).captureFrom(Array.get(rawArgument, i));
+    private ArgumentMatcherAction captureArgument() {
+        return new ArgumentMatcherAction() {
+            
+            @Override
+            public boolean apply(ArgumentMatcher<?> matcher, Object argument) {
+                if (matcher instanceof CapturesArguments) {
+                    ((CapturesArguments) matcher).captureFrom(argument);
                 }
+                
+                return true;
             }
-        }
-    }
-
-    private int regularArgumentsSize(Invocation invocation) {
-        return invocation.getMethod().isVarArgs() ?
-                invocation.getRawArguments().length - 1 // ignores vararg holder array
-                : matchers.size();
-    }
-
-    private Set<ArgumentMatcher> uniqueMatcherSet(int indexOfVararg) {
-        HashSet<ArgumentMatcher> set = new HashSet<ArgumentMatcher>();
-        for (int position = indexOfVararg; position < matchers.size(); position++) {
-            ArgumentMatcher matcher = matchers.get(position);
-            set.add(matcher);
-        }
-        return set;
+        };
     }
-
-    public static List<InvocationMatcher> createFrom(List<Invocation> invocations) {
-        LinkedList<InvocationMatcher> out = new LinkedList<InvocationMatcher>();
-        for (Invocation i : invocations) {
-            out.add(new InvocationMatcher(i));
-        }
-        return out;
+    
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private boolean argumentsMatch(Invocation actual) {
+        List matchers = getMatchers();
+        return getMatcherApplicationStrategyFor(actual, matchers).forEachMatcherAndArgument( matchesTypeSafe());
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java
new file mode 100644
index 0000000000..40c6519c91
--- /dev/null
+++ b/src/main/java/org/mockito/internal/invocation/MatcherApplicationStrategy.java
@@ -0,0 +1,128 @@
+package org.mockito.internal.invocation;
+
+import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
+import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
+import static org.mockito.internal.invocation.MatcherApplicationStrategy.MatcherApplicationType.ONE_MATCHER_PER_ARGUMENT;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.mockito.ArgumentMatcher;
+import org.mockito.internal.matchers.CapturingMatcher;
+import org.mockito.internal.matchers.VarargMatcher;
+import org.mockito.invocation.Invocation;
+
+public class MatcherApplicationStrategy {
+
+    private final Invocation invocation;
+    private final List<ArgumentMatcher<?>> matchers;
+    private final MatcherApplicationType matchingType;
+    
+
+
+    private MatcherApplicationStrategy(Invocation invocation, List<ArgumentMatcher<?>> matchers, MatcherApplicationType matchingType) {
+        this.invocation = invocation;
+        if (matchingType == MATCH_EACH_VARARGS_WITH_LAST_MATCHER) {
+            int times = varargLength(invocation);
+            this.matchers = appendLastMatcherNTimes(matchers, times);
+        } else {
+            this.matchers = matchers;
+        }
+
+        this.matchingType = matchingType;
+    }
+
+    /**
+     * Returns the {@link MatcherApplicationStrategy} that must be used to capture the
+     * arguments of the given <b>invocation</b> using the given <b>matchers</b>.
+     * 
+     * @param invocation
+     *            that contain the arguments to capture
+     * @param matchers
+     *            that will be used to capture the arguments of the invocation,
+     *            the passed {@link List} is not required to contain a
+     *            {@link CapturingMatcher}
+     * @return never <code>null</code>
+     */
+    public static MatcherApplicationStrategy getMatcherApplicationStrategyFor(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
+
+        MatcherApplicationType type = getMatcherApplicationType(invocation, matchers);
+        return new MatcherApplicationStrategy(invocation, matchers, type);
+    }
+
+    /**
+     * Applies the given {@link ArgumentMatcherAction} to all arguments and
+     * corresponding matchers
+     * 
+     * @param action
+     *            must not be <code>null</code>
+     * @return
+     *         <ul>
+     *         <li><code>true</code> if the given <b>action</b> returned
+     *         <code>true</code> for all arguments and matchers passed to it.
+     *         <li><code>false</code> if the given <b>action</b> returned
+     *         <code>false</code> for one of the passed arguments and matchers
+     *         <li><code>false</code> if the given matchers don't fit to the given invocation
+     *         because too many or to few matchers are available.
+     *         </ul>
+     */
+    public boolean forEachMatcherAndArgument(ArgumentMatcherAction action) {
+        if (matchingType == ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS)
+            return false;
+
+        Object[] arguments = invocation.getArguments();
+        for (int i = 0; i < arguments.length; i++) {
+            ArgumentMatcher<?> matcher = matchers.get(i);
+            Object argument = arguments[i];
+
+            if (!action.apply(matcher, argument)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static MatcherApplicationType getMatcherApplicationType(Invocation invocation, List<ArgumentMatcher<?>> matchers) {
+        final int rawArguments = invocation.getRawArguments().length;
+        final int expandedArguments = invocation.getArguments().length;
+        final int matcherCount = matchers.size();
+
+        if (expandedArguments == matcherCount) {
+            return ONE_MATCHER_PER_ARGUMENT;
+        }
+
+        if (rawArguments == matcherCount && isLastMatcherVargargMatcher(matchers)) {
+            return MATCH_EACH_VARARGS_WITH_LAST_MATCHER;
+        }
+
+        return ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
+    }
+
+    private static boolean isLastMatcherVargargMatcher(final List<ArgumentMatcher<?>> matchers) {
+        return lastMatcher(matchers) instanceof VarargMatcher;
+    }
+
+    private static List<ArgumentMatcher<?>> appendLastMatcherNTimes(List<ArgumentMatcher<?>> matchers, int timesToAppendLastMatcher) {
+        ArgumentMatcher<?> lastMatcher = lastMatcher(matchers);
+
+        List<ArgumentMatcher<?>> expandedMatchers = new ArrayList<ArgumentMatcher<?>>(matchers);
+        for (int i = 0; i < timesToAppendLastMatcher; i++) {
+            expandedMatchers.add(lastMatcher);
+        }
+        return expandedMatchers;
+    }
+
+    private static int varargLength(Invocation invocation) {
+        int rawArgumentCount = invocation.getRawArguments().length;
+        int expandedArgumentCount = invocation.getArguments().length;
+        return expandedArgumentCount - rawArgumentCount;
+    }
+
+    private static ArgumentMatcher<?> lastMatcher(List<ArgumentMatcher<?>> matchers) {
+        return matchers.get(matchers.size() - 1);
+    }
+
+    enum MatcherApplicationType {
+        ONE_MATCHER_PER_ARGUMENT, MATCH_EACH_VARARGS_WITH_LAST_MATCHER, ERROR_UNSUPPORTED_NUMBER_OF_MATCHERS;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java
new file mode 100644
index 0000000000..181e47fea9
--- /dev/null
+++ b/src/main/java/org/mockito/internal/invocation/TypeSafeMatching.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.invocation;
+
+import java.lang.reflect.Method;
+
+import org.mockito.ArgumentMatcher;
+
+@SuppressWarnings({"unchecked","rawtypes"})
+public class TypeSafeMatching implements ArgumentMatcherAction {
+
+    private final static ArgumentMatcherAction TYPE_SAFE_MATCHING_ACTION = new TypeSafeMatching();
+        
+    private TypeSafeMatching() {}
+
+    
+    public static ArgumentMatcherAction matchesTypeSafe(){
+        return TYPE_SAFE_MATCHING_ACTION;
+    }
+    @Override
+    public boolean apply(ArgumentMatcher matcher, Object argument) {
+        return isCompatible(matcher, argument) && matcher.matches(argument);
+    }
+
+    
+    /**
+     * Returns <code>true</code> if the given <b>argument</b> can be passed to
+     * the given <code>argumentMatcher</code> without causing a
+     * {@link ClassCastException}.
+     */
+    private static boolean isCompatible(ArgumentMatcher<?> argumentMatcher, Object argument) {
+        if (argument == null)
+            return true;
+
+        Class<?> expectedArgumentType = getArgumentType(argumentMatcher);
+
+        return expectedArgumentType.isInstance(argument);
+    }
+
+    /**
+     * Returns the type of {@link ArgumentMatcher#matches(Object)} of the given
+     * {@link ArgumentMatcher} implementation.
+     */
+    private static Class<?> getArgumentType(ArgumentMatcher<?> argumentMatcher) {
+        Method[] methods = argumentMatcher.getClass().getMethods();
+        
+        for (Method method : methods) {
+            if (isMatchesMethod(method)) {
+                return method.getParameterTypes()[0];
+            }
+        }
+        throw new NoSuchMethodError("Method 'matches(T)' not found in ArgumentMatcher: " + argumentMatcher + " !\r\n Please file a bug with this stack trace at: https://github.com/mockito/mockito/issues/new ");
+    }
+
+    /**
+     * Returns <code>true</code> if the given method is
+     * {@link ArgumentMatcher#matches(Object)}
+     */
+    private static boolean isMatchesMethod(Method method) {
+        if (method.getParameterTypes().length != 1) {
+            return false;
+        }
+        if (method.isBridge()) {
+            return false;
+        }
+        return method.getName().equals("matches");
+    }
+}
\ No newline at end of file


Test patch:
diff --git a/src/test/java/org/mockito/internal/invocation/ArgumentsComparatorTest.java b/src/test/java/org/mockito/internal/invocation/ArgumentsComparatorTest.java
deleted file mode 100644
index 9aed7c77e7..0000000000
--- a/src/test/java/org/mockito/internal/invocation/ArgumentsComparatorTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2007 Mockito contributors
- * This program is made available under the terms of the MIT License.
- */
-package org.mockito.internal.invocation;
-
-import static java.util.Arrays.asList;
-import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertTrue;
-import static org.mockito.internal.invocation.ArgumentsComparator.argumentsMatch;
-import static org.mockito.internal.matchers.Any.ANY;
-
-import java.util.List;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.internal.matchers.Any;
-import org.mockito.internal.matchers.Equals;
-import org.mockito.internal.matchers.InstanceOf;
-import org.mockito.invocation.Invocation;
-import org.mockitousage.IMethods;
-import org.mockitoutil.TestBase;
-
-@SuppressWarnings("unchecked")
-public class ArgumentsComparatorTest extends TestBase {
-
-    @Mock IMethods mock;
-
-    @Test
-    public void shouldKnowWhenArgumentsMatch() {
-        //given
-        Invocation invocation = new InvocationBuilder().args("1", 100).toInvocation();
-        InvocationMatcher invocationMatcher = new InvocationBuilder().args("1", 100).toInvocationMatcher();
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-
-    @Test
-    public void shouldKnowWhenArgsDifferent() {
-        //given
-        Invocation invocation = new InvocationBuilder().args("1", 100).toInvocation();
-        InvocationMatcher invocationMatcher = new InvocationBuilder().args("100", 100).toInvocationMatcher();
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldKnowWhenActualArgsSizeIsDifferent() {
-        //given
-        Invocation invocation = new InvocationBuilder().args("100", 100).toInvocation();
-        InvocationMatcher invocationMatcher = new InvocationBuilder().args("100").toInvocationMatcher();
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldKnowWhenMatchersSizeIsDifferent() {
-        //given
-        Invocation invocation = new InvocationBuilder().args("100").toInvocation();
-        InvocationMatcher invocationMatcher = new InvocationBuilder().args("100", 100).toInvocationMatcher();
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldKnowWhenVarargsMatch() {
-        //given
-        mock.varargs("1", "2", "3");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals("1"), Any.ANY, new InstanceOf(String.class)));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-
-    @Test
-    public void shouldKnowWhenVarargsDifferent() {
-        //given
-        mock.varargs("1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals("100"), Any.ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldAllowAnyVarargMatchEntireVararg() {
-        //given
-        mock.varargs("1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-
-    @Test
-    public void shouldNotAllowAnyObjectWithMixedVarargs() {
-        //given
-        mock.mixedVarargs(1, "1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals(1)));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldAllowAnyObjectWithMixedVarargs() {
-        //given
-        mock.mixedVarargs(1, "1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals(1), ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-
-    @Test
-    public void shouldNotMatchWhenSomeOtherArgumentDoesNotMatch() {
-        //given
-        mock.mixedVarargs(1, "1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals(100), ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldAnyObjectVarargDealWithDifferentSizeOfArgs() {
-        //given
-        mock.mixedVarargs(1, "1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals(1)));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertFalse(match);
-    }
-
-    @Test
-    public void shouldMatchAnyVarargEvenIfOneOfTheArgsIsNull() {
-        //given
-        mock.mixedVarargs(null, null, "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List) asList(new Equals(null), ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-
-    @Test
-    public void shouldMatchAnyVarargEvenIfMatcherIsDecorated() {
-        //given
-        mock.varargs("1", "2");
-        Invocation invocation = getLastInvocation();
-        InvocationMatcher invocationMatcher = new InvocationMatcher(invocation, (List)asList(ANY));
-
-        //when
-        boolean match = argumentsMatch(invocationMatcher, invocation);
-
-        //then
-        assertTrue(match);
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java
new file mode 100644
index 0000000000..6a0c6c696e
--- /dev/null
+++ b/src/test/java/org/mockito/internal/invocation/MatcherApplicationStrategyTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.invocation;
+
+import static java.util.Arrays.asList;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.internal.invocation.MatcherApplicationStrategy.getMatcherApplicationStrategyFor;
+import static org.mockito.internal.matchers.Any.ANY;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.internal.matchers.Any;
+import org.mockito.internal.matchers.Equals;
+import org.mockito.internal.matchers.InstanceOf;
+import org.mockito.invocation.Invocation;
+import org.mockitousage.IMethods;
+import org.mockitoutil.TestBase;
+
+@SuppressWarnings("unchecked")
+public class MatcherApplicationStrategyTest extends TestBase {
+
+    @Mock
+    IMethods mock;
+    private Invocation invocation;
+    private List matchers;
+
+    private RecordingAction recordAction;
+
+    @Before
+    public void before() {
+        recordAction = new RecordingAction();
+    }
+    
+    @Test
+    public void shouldKnowWhenActualArgsSizeIsDifferent1() {
+        // given
+        invocation = varargs("1");
+        matchers = asList(new Equals("1"));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(RETURN_ALWAYS_FALSE);
+
+        // then
+        assertFalse(match);
+    }
+    
+    @Test
+    public void shouldKnowWhenActualArgsSizeIsDifferent2() {
+        // given
+        invocation = varargs("1");
+        matchers = asList(new Equals("1"));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(RETURN_ALWAYS_TRUE);
+
+        // then
+        assertTrue(match);
+    }
+
+    @Test
+    public void shouldKnowWhenActualArgsSizeIsDifferent() {
+        // given
+        invocation = varargs("1", "2");
+        matchers = asList(new Equals("1"));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(RETURN_ALWAYS_TRUE);
+
+        // then
+        assertFalse(match);
+    }
+
+    @Test
+    public void shouldKnowWhenMatchersSizeIsDifferent() {
+        // given
+        invocation = varargs("1");
+        matchers = asList(new Equals("1"), new Equals("2"));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(RETURN_ALWAYS_TRUE);
+
+        // then
+        assertFalse(match);
+    }
+
+    @Test
+    public void shouldKnowWhenVarargsMatch() {
+        // given
+        invocation = varargs("1", "2", "3");
+        matchers = asList(new Equals("1"), Any.ANY, new InstanceOf(String.class));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        assertTrue(match);
+    }
+
+    @Test
+    public void shouldAllowAnyVarargMatchEntireVararg() {
+        // given
+        invocation = varargs("1", "2");
+        matchers = asList(ANY);
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        assertTrue(match);
+    }
+
+    @Test
+    public void shouldNotAllowAnyObjectWithMixedVarargs() {
+        // given
+        invocation = mixedVarargs(1, "1", "2");
+        matchers = asList(new Equals(1));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        assertFalse(match);
+    }
+
+    @Test
+    public void shouldAllowAnyObjectWithMixedVarargs() {
+        // given
+        invocation = mixedVarargs(1, "1", "2");
+        matchers = asList(new Equals(1), ANY);
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        assertTrue(match);
+    }
+
+    @Test
+    public void shouldAnyObjectVarargDealWithDifferentSizeOfArgs() {
+        // given
+        invocation = mixedVarargs(1, "1", "2");
+        matchers = asList(new Equals(1));
+
+        // when
+        boolean match = getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        assertFalse(match);
+
+        recordAction.assertIsEmpty();
+    }
+
+    @Test
+    public void shouldMatchAnyVarargEvenIfOneOfTheArgsIsNull() {
+        // given
+        invocation = mixedVarargs(null, null, "2");
+        matchers = asList(new Equals(null), ANY);
+
+        // when
+        getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        recordAction.assertContainsExactly(new Equals(null), ANY, ANY);
+
+    }
+
+    @Test
+    public void shouldMatchAnyVarargEvenIfMatcherIsDecorated() {
+        // given
+        invocation = varargs("1", "2");
+        matchers = asList(ANY);
+
+        // when
+        getMatcherApplicationStrategyFor(invocation, matchers).forEachMatcherAndArgument(recordAction);
+
+        // then
+        recordAction.assertContainsExactly(ANY, ANY);
+    }
+
+    private Invocation mixedVarargs(Object a, String... s) {
+        mock.mixedVarargs(a, s);
+        return getLastInvocation();
+    }
+
+    private Invocation varargs(String... s) {
+        mock.varargs(s);
+        return getLastInvocation();
+    }
+
+    private class RecordingAction implements ArgumentMatcherAction {
+        private List<ArgumentMatcher<?>> matchers = new ArrayList<ArgumentMatcher<?>>();
+
+        @Override
+        public boolean apply(ArgumentMatcher<?> matcher, Object argument) {
+            matchers.add(matcher);
+            return true;
+        }
+
+        public void assertIsEmpty() {
+            assertThat(matchers).isEmpty();
+        }
+
+        public void assertContainsExactly(ArgumentMatcher<?>... matchers) {
+            assertThat(this.matchers).containsExactly(matchers);
+        }
+    }
+
+    private static final ArgumentMatcherAction RETURN_ALWAYS_TRUE = new ArgumentMatcherAction() {
+        @Override
+        public boolean apply(ArgumentMatcher<?> matcher, Object argument) {
+            return true;
+        }
+    };
+    
+    private static final ArgumentMatcherAction RETURN_ALWAYS_FALSE = new ArgumentMatcherAction() {
+        @Override
+        public boolean apply(ArgumentMatcher<?> matcher, Object argument) {
+            return false;
+        }
+    };
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/mockito/internal/invocation/TypeSafeMatchingTest.java b/src/test/java/org/mockito/internal/invocation/TypeSafeMatchingTest.java
new file mode 100644
index 0000000000..39deb6796f
--- /dev/null
+++ b/src/test/java/org/mockito/internal/invocation/TypeSafeMatchingTest.java
@@ -0,0 +1,163 @@
+package org.mockito.internal.invocation;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.internal.invocation.TypeSafeMatching.matchesTypeSafe;
+
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.internal.matchers.LessOrEqual;
+import org.mockito.internal.matchers.Null;
+import org.mockito.internal.matchers.StartsWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockitousage.IMethods;
+
+public class TypeSafeMatchingTest {
+
+    private static final Object NOT_A_COMPARABLE = new Object();
+
+    @Rule
+    public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+    @Mock
+    public IMethods mock;
+
+    /**
+     * Should not throw an {@link NullPointerException}
+     * 
+     * @see Bug-ID https://github.com/mockito/mockito/issues/457
+     */
+    @Test
+    public void compareNullArgument() {
+        boolean match = matchesTypeSafe().apply(new LessOrEqual<Integer>(5), null);
+        assertThat(match).isFalse();
+
+    }
+
+    /**
+     * Should not throw an {@link ClassCastException}
+     */
+    @Test
+    public void compareToNonCompareable() {
+        boolean match = matchesTypeSafe().apply(new LessOrEqual<Integer>(5), NOT_A_COMPARABLE);
+        assertThat(match).isFalse();
+    }
+
+    /**
+     * Should not throw an {@link ClassCastException}
+     */
+    @Test
+    public void compareToNull() {
+        boolean match = matchesTypeSafe().apply(new LessOrEqual<Integer>(null), null);
+        assertThat(match).isFalse();
+    }
+
+    /**
+     * Should not throw an {@link ClassCastException}
+     */
+    @Test
+    public void compareToNull2() {
+        boolean match = matchesTypeSafe().apply(Null.NULL, null);
+        assertThat(match).isTrue();
+    }
+
+    /**
+     * Should not throw an {@link ClassCastException}
+     */
+    @Test
+    public void compareToStringVsInt() {
+        boolean match = matchesTypeSafe().apply(new StartsWith("Hello"), 123);
+        assertThat(match).isFalse();
+    }
+
+    @Test
+    public void compareToIntVsString() throws Exception {
+        boolean match = matchesTypeSafe().apply(new LessOrEqual<Integer>(5), "Hello");
+        assertThat(match).isFalse();
+    }
+
+    @Test
+    public void matchesOverloadsMustBeIgnored() {
+        class TestMatcher implements ArgumentMatcher<Integer> {
+            @Override
+            public boolean matches(Integer arg) {
+                return false;
+            }
+
+            @SuppressWarnings("unused")
+            public boolean matches(Date arg) {
+                throw new UnsupportedOperationException();
+            }
+
+            @SuppressWarnings("unused")
+            public boolean matches(Integer arg, Void v) {
+                throw new UnsupportedOperationException();
+            }
+
+        }
+
+        boolean match = matchesTypeSafe().apply(new TestMatcher(), 123);
+        assertThat(match).isFalse();
+    }
+
+    @Test
+    public void matchesWithSubTypeExtendingGenericClass() {
+        abstract class GenericMatcher<T> implements ArgumentMatcher<T> {
+        }
+        class TestMatcher extends GenericMatcher<Integer> {
+            @Override
+            public boolean matches(Integer argument) {
+                return true;
+            }
+        }
+        boolean match = matchesTypeSafe().apply(new TestMatcher(), 123);
+        assertThat(match).isTrue();
+    }
+
+    @Test
+    public void dontMatchesWithSubTypeExtendingGenericClass() {
+        final AtomicBoolean wasCalled = new AtomicBoolean();
+        
+        abstract class GenericMatcher<T> implements ArgumentMatcher<T> {
+        }
+        class TestMatcher extends GenericMatcher<Integer> {
+            @Override
+            public boolean matches(Integer argument) {
+                wasCalled.set(true);
+                return true;
+            }
+        }
+        wasCalled.set(false);
+        matchesTypeSafe().apply(new TestMatcher(), 123);
+        assertThat(wasCalled.get()).isTrue();
+
+        wasCalled.set(false);
+        matchesTypeSafe().apply(new TestMatcher(), "");
+        assertThat(wasCalled.get()).isFalse();
+    }
+
+    @Test
+    public void passEveryArgumentTypeIfNoBridgeMethodWasGenerated() {
+        final AtomicBoolean wasCalled = new AtomicBoolean();
+        class GenericMatcher<T> implements ArgumentMatcher<T> {
+            @Override
+            public boolean matches(T argument) {
+                wasCalled.set(true);
+                return true;
+            }
+        }
+
+        wasCalled.set(false);
+        matchesTypeSafe().apply(new GenericMatcher<Integer>(), 123);
+        assertThat(wasCalled.get()).isTrue();
+
+        wasCalled.set(false);
+        matchesTypeSafe().apply(new GenericMatcher<Integer>(), "");
+        assertThat(wasCalled.get()).isTrue();
+    }
+}
diff --git a/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java b/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
index 38cafecc82..185bfaea17 100644
--- a/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
+++ b/src/test/java/org/mockito/internal/util/reflection/FieldInitializerTest.java
@@ -6,6 +6,7 @@
 
 import org.junit.Test;
 import org.mockito.InjectMocks;
+import org.mockito.Mockito;
 import org.mockito.exceptions.base.MockitoException;
 import org.mockito.internal.util.reflection.FieldInitializer.ConstructorArgumentResolver;
 
@@ -156,7 +157,7 @@ public void should_not_fail_if_inner_class_field_is_instantiated() throws Except
 
     @Test
     public void can_instantiate_class_with_parameterized_constructor() throws Exception {
-        ConstructorArgumentResolver resolver = given(mock(ConstructorArgumentResolver.class).resolveTypeInstances(any(Class[].class)))
+        ConstructorArgumentResolver resolver = given(mock(ConstructorArgumentResolver.class).resolveTypeInstances(any(Class.class)))
                         .willReturn(new Object[]{null}).getMock();
 
         new FieldInitializer(this, field("noDefaultConstructor"), resolver).initialize();
diff --git a/src/test/java/org/mockitousage/matchers/VarargsTest.java b/src/test/java/org/mockitousage/matchers/VarargsTest.java
new file mode 100644
index 0000000000..6720ca5ff0
--- /dev/null
+++ b/src/test/java/org/mockitousage/matchers/VarargsTest.java
@@ -0,0 +1,341 @@
+package org.mockitousage.matchers;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.assertj.core.api.AbstractListAssert;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.api.Condition;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.exceptions.verification.junit.ArgumentsAreDifferent;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockitousage.IMethods;
+
+public class VarargsTest {
+
+    @Rule
+    public MockitoRule mockitoRule = MockitoJUnit.rule();
+    @Rule
+    public ExpectedException exception = ExpectedException.none();
+    @Captor
+    private ArgumentCaptor<String> captor;
+    @Mock
+    private IMethods mock;
+
+    private static final Condition<Object> NULL = new Condition<Object>() {
+
+        @Override
+        public boolean matches(Object value) {
+            return value == null;
+        }
+    };
+
+    @Test
+    public void shouldMatchVarArgs_noArgs() {
+        mock.varargs();
+
+        verify(mock).varargs();
+    }
+
+    @Test
+    @Ignore("This test must succeed but is fails currently, see github issue #616")
+    public void shouldMatchEmptyVarArgs_noArgsIsNotNull() {
+        mock.varargs();
+
+        verify(mock).varargs(isNotNull());
+    }
+
+    @Test
+    @Ignore("This test must succeed but is fails currently, see github issue #616")
+    public void shouldMatchEmptyVarArgs_noArgsIsNull() {
+        mock.varargs();
+
+        verify(mock).varargs(isNull());
+    }
+
+    @Test
+    @Ignore("This test must succeed but is fails currently, see github issue #616")
+    public void shouldMatchEmptyVarArgs_noArgsIsNotNullArray() {
+        mock.varargs();
+
+        verify(mock).varargs((String[]) isNotNull());
+    }
+
+    @Test
+    public void shouldMatchVarArgs_oneNullArg_eqNull() {
+        Object arg = null;
+        mock.varargs(arg);
+
+        verify(mock).varargs(eq(null));
+    }
+
+    @Test
+    public void shouldMatchVarArgs_oneNullArg_isNull() {
+        Object arg = null;
+        mock.varargs(arg);
+
+        verify(mock).varargs(isNull());
+    }
+
+    @Test
+    public void shouldMatchVarArgs_nullArrayArg() {
+        Object[] argArray = null;
+        mock.varargs(argArray);
+
+        verify(mock).varargs(isNull());
+    }
+
+    @Test
+    public void shouldnotMatchVarArgs_twoArgsOneMatcher() {
+        mock.varargs("1", "1");
+
+        exception.expectMessage("Argument(s) are different");
+
+        verify(mock).varargs(eq("1"));
+    }
+
+    @Test
+    public void shouldMatchVarArgs_emptyVarArgsOneAnyMatcher() {
+        mock.varargs();
+
+        verify(mock).varargs((String[])any()); // any() -> VarargMatcher
+    }
+    
+    @Test
+    public void shouldMatchVarArgs_oneArgsOneAnyMatcher() {
+        mock.varargs(1);
+
+        verify(mock).varargs(any()); // any() -> VarargMatcher
+    }
+    
+    @Test
+    public void shouldMatchVarArgs_twoArgsOneAnyMatcher() {
+        mock.varargs(1, 2);
+
+        verify(mock).varargs(any()); // any() -> VarargMatcher
+    }
+
+    @Test
+    public void shouldMatchVarArgs_twoArgsTwoAnyMatcher() {
+        mock.varargs(1, 2);
+
+        verify(mock).varargs(any(), any()); // any() -> VarargMatcher
+    }
+
+    @Test
+    public void shouldMatchVarArgs_twoArgsThreeAnyMatcher() {
+        mock.varargs(1, 2);
+
+        exception.expectMessage("Argument(s) are different");
+
+        verify(mock).varargs(any(), any(), any()); //any() -> VarargMatcher
+    }
+
+    @Test
+    public void shouldMatchVarArgs_oneNullArgument() {
+        mock.varargs("1", null);
+
+        verify(mock).varargs(eq("1"), (String) isNull());
+    }
+
+    @Test
+    public void shouldMatchVarArgs_onebyte() {
+        mock.varargsbyte((byte) 1);
+
+        verify(mock).varargsbyte(eq((byte) 1));
+    }
+
+    @Test
+    public void shouldMatchVarArgs_nullByteArray() {
+        mock.varargsbyte(null);
+
+        verify(mock).varargsbyte((byte[]) isNull());
+    }
+
+    @Test
+    public void shouldMatchVarArgs_emptyByteArray() {
+        mock.varargsbyte();
+
+        verify(mock).varargsbyte();
+    }
+
+    @Test
+    @Ignore
+    public void shouldMatchEmptyVarArgs_emptyArrayIsNotNull() {
+        mock.varargsbyte();
+
+        verify(mock).varargsbyte((byte[]) isNotNull());
+    }
+
+    @Test
+    public void shouldMatchVarArgs_oneArgIsNotNull() {
+        mock.varargsbyte((byte) 1);
+
+        verify(mock).varargsbyte((byte[]) isNotNull());
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_noArgs() {
+        mock.varargs();
+
+        verify(mock).varargs(captor.capture());
+
+        assertThat(captor).isEmpty();
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_oneNullArg_eqNull() {
+        String arg = null;
+        mock.varargs(arg);
+
+        verify(mock).varargs(captor.capture());
+
+        assertThat(captor).areExactly(1, NULL);
+    }
+
+    /**
+     * Relates to Github issue #583 "ArgumentCaptor: NPE when an null array is
+     * passed to a varargs method"
+     */
+    @Test
+    public void shouldCaptureVarArgs_nullArrayArg() {
+        String[] argArray = null;
+        mock.varargs(argArray);
+
+        verify(mock).varargs(captor.capture());
+        assertThat(captor).areExactly(1, NULL);
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_twoArgsOneCapture() {
+        mock.varargs("1", "2");
+
+        verify(mock).varargs(captor.capture());
+
+        assertThat(captor).contains("1", "2");
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_twoArgsTwoCaptures() {
+        mock.varargs("1", "2");
+
+        verify(mock).varargs(captor.capture(), captor.capture());
+
+        assertThat(captor).contains("1", "2");
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_oneNullArgument() {
+        mock.varargs("1", null);
+
+        verify(mock).varargs(captor.capture());
+
+        assertThat(captor).contains("1", (String) null);
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_oneNullArgument2() {
+        mock.varargs("1", null);
+
+        verify(mock).varargs(captor.capture(), captor.capture());
+
+        assertThat(captor).contains("1", (String) null);
+    }
+
+    @Test
+    public void shouldNotCaptureVarArgs_3args2captures() {
+        mock.varargs("1", "2", "3");
+
+        exception.expect(ArgumentsAreDifferent.class);
+
+        verify(mock).varargs(captor.capture(), captor.capture());
+
+    }
+
+    @Test
+    public void shouldCaptureVarArgs_3argsCaptorMatcherMix() {
+        mock.varargs("1", "2", "3");
+
+        verify(mock).varargs(captor.capture(), eq("2"), captor.capture());
+
+        assertThat(captor).containsExactly("1", "3");
+
+    }
+
+    @Test
+    public void shouldNotCaptureVarArgs_3argsCaptorMatcherMix() {
+        mock.varargs("1", "2", "3");
+
+        try {
+            verify(mock).varargs(captor.capture(), eq("X"), captor.capture());
+            fail("The verification must fail, cause the second arg was not 'X' as expected!");
+        } catch (ArgumentsAreDifferent expected) {
+        }
+
+        assertThat(captor).isEmpty();
+
+    }
+
+    @Test
+    public void shouldNotCaptureVarArgs_1args2captures() {
+        mock.varargs("1");
+
+        exception.expect(ArgumentsAreDifferent.class);
+
+        verify(mock).varargs(captor.capture(), captor.capture());
+
+    }
+
+    /**
+     * As of v2.0.0-beta.118 this test fails. Once the github issues:
+     * <ul>
+     * <li>'#584 ArgumentCaptor can't capture varargs-arrays
+     * <li>#565 ArgumentCaptor should be type aware' are fixed this test must
+     * succeed
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    @Test
+    @Ignore("Blocked by github issue: #584 & #565")
+    public void shouldCaptureVarArgsAsArray() {
+        mock.varargs("1", "2");
+
+        ArgumentCaptor<String[]> varargCaptor = ArgumentCaptor.forClass(String[].class);
+
+        verify(mock).varargs(varargCaptor.capture());
+
+        assertThat(varargCaptor).containsExactly(new String[] { "1", "2" });
+    }
+
+    @Test
+    public void shouldNotMatchRegualrAndVaraArgs()   {
+        mock.varargsString(1, "a","b");
+        
+        exception.expect(ArgumentsAreDifferent.class);
+        
+        verify(mock).varargsString(1);
+    }
+    @Test
+    public void shouldNotMatchVaraArgs()   {
+        when(mock.varargsObject(1, "a","b")).thenReturn("OK");
+        
+        Assertions.assertThat(mock.varargsObject(1)).isNull();
+    }
+
+    private static <T> AbstractListAssert<?, ?, T> assertThat(ArgumentCaptor<T> captor) {
+        return Assertions.assertThat(captor.getAllValues());
+    }
+}
\ No newline at end of file