How to use findAllFieldsUsingStrategy method of org.powermock.reflect.internal.WhiteboxImpl class

Best Powermock code snippet using org.powermock.reflect.internal.WhiteboxImpl.findAllFieldsUsingStrategy

Source:WhiteboxImpl.java Github

copy

Full Screen

...429 }430 foundField.setAccessible(true);431 return foundField;432 }433 private static Set<Field> findAllFieldsUsingStrategy(FieldMatcherStrategy strategy, Object object, boolean checkHierarchy, Class<?> startClass) {434 if (object == null) {435 throw new IllegalArgumentException("The object containing the field cannot be null");436 }437 final Set<Field> foundFields = new LinkedHashSet<Field>();438 while (startClass != null) {439 final Field[] declaredFields = startClass.getDeclaredFields();440 for (Field field : declaredFields) {441 if (strategy.matches(field) && hasFieldProperModifier(object, field)) {442 field.setAccessible(true);443 foundFields.add(field);444 }445 }446 if (!checkHierarchy) {447 break;448 }449 startClass = startClass.getSuperclass();450 }451 return Collections.unmodifiableSet(foundFields);452 }453 private static boolean hasFieldProperModifier(Object object, Field field) {454 return ((object instanceof Class<?> && Modifier.isStatic(field.getModifiers())) || ((object instanceof Class<?> == false && Modifier455 .isStatic(field.getModifiers()) == false)));456 }457 /**458 * Get the value of a field using reflection. This method will traverse the459 * super class hierarchy until the first field of type <tt>fieldType</tt> is460 * found. The value of this field will be returned.461 * 462 * @param object463 * the object to modify464 * @param fieldType465 * the type of the field466 */467 @SuppressWarnings("unchecked")468 public static <T> T getInternalState(Object object, Class<T> fieldType) {469 Field foundField = findFieldInHierarchy(object, new AssignableFromFieldTypeMatcherStrategy(fieldType));470 try {471 return (T) foundField.get(object);472 } catch (IllegalAccessException e) {473 throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e);474 }475 }476 /**477 * Get the value of a field using reflection. Use this method when you need478 * to specify in which class the field is declared. The first field matching479 * the <tt>fieldType</tt> in <tt>where</tt> will is the field whose value480 * will be returned.481 * 482 * @param <T>483 * the expected type of the field484 * @param object485 * the object to modify486 * @param fieldType487 * the type of the field488 * @param where489 * which class the field is defined490 */491 @SuppressWarnings("unchecked")492 public static <T> T getInternalState(Object object, Class<T> fieldType, Class<?> where) {493 if (object == null) {494 throw new IllegalArgumentException("object and type are not allowed to be null");495 }496 try {497 return (T) findFieldOrThrowException(fieldType, where).get(object);498 } catch (IllegalAccessException e) {499 throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e);500 }501 }502 /**503 * Get the value of a field using reflection. Use this method when you need504 * to specify in which class the field is declared. This might be useful505 * when you have mocked the instance you are trying to access. Use this506 * method to avoid casting.507 * 508 * @param <T>509 * the expected type of the field510 * @param object511 * the object to modify512 * @param fieldName513 * the name of the field514 * @param where515 * which class the field is defined516 */517 @SuppressWarnings("unchecked")518 public static <T> T getInternalState(Object object, String fieldName, Class<?> where) {519 if (object == null || fieldName == null || fieldName.equals("") || fieldName.startsWith(" ")) {520 throw new IllegalArgumentException("object, field name, and \"where\" must not be empty or null.");521 }522 Field field = null;523 try {524 field = where.getDeclaredField(fieldName);525 field.setAccessible(true);526 return (T) field.get(object);527 } catch (NoSuchFieldException e) {528 throw new FieldNotFoundException("Field '" + fieldName + "' was not found in class " + where.getName() + ".");529 } catch (Exception e) {530 throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e);531 }532 }533 /**534 * Invoke a private or inner class method without the need to specify the535 * method name. This is thus a more refactor friendly version of the536 * {@link #invokeMethod(Object, String, Object...)} method and is recommend537 * over this method for that reason. This method might be useful to test538 * private methods.539 * 540 * @throws Throwable541 */542 @SuppressWarnings("unchecked")543 public static synchronized <T> T invokeMethod(Object tested, Object... arguments) throws Exception {544 return (T) doInvokeMethod(tested, null, null, arguments);545 }546 /**547 * Invoke a private or inner class method without the need to specify the548 * method name. This is thus a more refactor friendly version of the549 * {@link #invokeMethod(Object, String, Object...)} method and is recommend550 * over this method for that reason. This method might be useful to test551 * private methods.552 */553 @SuppressWarnings("unchecked")554 public static synchronized <T> T invokeMethod(Class<?> tested, Object... arguments) throws Exception {555 return (T) doInvokeMethod(tested, null, null, arguments);556 }557 /**558 * Invoke a private or inner class method. This might be useful to test559 * private methods.560 * 561 * @throws Throwable562 */563 @SuppressWarnings("unchecked")564 public static synchronized <T> T invokeMethod(Object tested, String methodToExecute, Object... arguments) throws Exception {565 return (T) doInvokeMethod(tested, null, methodToExecute, arguments);566 }567 /**568 * Invoke a private or inner class method in cases where power mock cannot569 * automatically determine the type of the parameters, for example when570 * mixing primitive types and wrapper types in the same method. For most571 * situations use {@link #invokeMethod(Class, String, Object...)} instead.572 * 573 * @throws Exception574 * Exception that may occur when invoking this method.575 */576 @SuppressWarnings("unchecked")577 public static synchronized <T> T invokeMethod(Object tested, String methodToExecute, Class<?>[] argumentTypes, Object... arguments)578 throws Exception {579 final Class<?> unmockedType = getType(tested);580 Method method = getMethod(unmockedType, methodToExecute, argumentTypes);581 if (method == null) {582 throwExceptionIfMethodWasNotFound(unmockedType, methodToExecute, null, arguments);583 }584 return (T) performMethodInvocation(tested, method, arguments);585 }586 /**587 * Invoke a private or inner class method in a subclass (defined by588 * <code>definedIn</code>) in cases where power mock cannot automatically589 * determine the type of the parameters, for example when mixing primitive590 * types and wrapper types in the same method. For most situations use591 * {@link #invokeMethod(Class, String, Object...)} instead.592 * 593 * @throws Exception594 * Exception that may occur when invoking this method.595 */596 @SuppressWarnings("unchecked")597 public static synchronized <T> T invokeMethod(Object tested, String methodToExecute, Class<?> definedIn, Class<?>[] argumentTypes,598 Object... arguments) throws Exception {599 Method method = getMethod(definedIn, methodToExecute, argumentTypes);600 if (method == null) {601 throwExceptionIfMethodWasNotFound(definedIn, methodToExecute, null, arguments);602 }603 return (T) performMethodInvocation(tested, method, arguments);604 }605 /**606 * Invoke a private or inner class method in that is located in a subclass607 * of the tested instance. This might be useful to test private methods.608 * 609 * @throws Exception610 * Exception that may occur when invoking this method.611 */612 @SuppressWarnings("unchecked")613 public static synchronized <T> T invokeMethod(Object tested, Class<?> declaringClass, String methodToExecute, Object... arguments)614 throws Exception {615 return (T) doInvokeMethod(tested, declaringClass, methodToExecute, arguments);616 }617 /**618 * Invoke a private method in that is located in a subclass of an instance.619 * This might be useful to test overloaded private methods.620 * <p>621 * Use this for overloaded methods only, if possible use622 * {@link #invokeMethod(Object, Object...)} or623 * {@link #invokeMethod(Object, String, Object...)} instead.624 * 625 * @throws Exception626 * Exception that may occur when invoking this method.627 */628 @SuppressWarnings("unchecked")629 public static synchronized <T> T invokeMethod(Object object, Class<?> declaringClass, String methodToExecute, Class<?>[] parameterTypes,630 Object... arguments) throws Exception {631 if (object == null) {632 throw new IllegalArgumentException("object cannot be null");633 }634 final Method methodToInvoke = getMethod(declaringClass, methodToExecute, parameterTypes);635 // Invoke method636 return (T) performMethodInvocation(object, methodToInvoke, arguments);637 }638 /**639 * Invoke a private or inner class method. This might be useful to test640 * private methods.641 * 642 */643 @SuppressWarnings("unchecked")644 public static synchronized <T> T invokeMethod(Class<?> clazz, String methodToExecute, Object... arguments) throws Exception {645 return (T) doInvokeMethod(clazz, null, methodToExecute, arguments);646 }647 @SuppressWarnings("unchecked")648 private static <T> T doInvokeMethod(Object tested, Class<?> declaringClass, String methodToExecute, Object... arguments) throws Exception {649 Method methodToInvoke = findMethodOrThrowException(tested, declaringClass, methodToExecute, arguments);650 // Invoke test651 return (T) performMethodInvocation(tested, methodToInvoke, arguments);652 }653 /**654 * Finds and returns a certain method. If the method couldn't be found this655 * method delegates to656 * {@link WhiteboxImpl#throwExceptionIfMethodWasNotFound(Object, String, Method, Object...)}657 * .658 * 659 * @param tested660 * The instance or class containing the method.661 * @param declaringClass662 * The class where the method is supposed to be declared (may be663 * <code>null</code>).664 * @param methodToExecute665 * The method name. If <code>null</code> then method will be666 * looked up based on the argument types only.667 * @param arguments668 * The arguments of the methods.669 * @return A single method.670 */671 public static Method findMethodOrThrowException(Object tested, Class<?> declaringClass, String methodToExecute, Object[] arguments) {672 if (tested == null) {673 throw new IllegalArgumentException("The object to perform the operation on cannot be null.");674 }675 /*676 * Get methods from the type if it's not mocked or from the super type677 * if the tested object is mocked.678 */679 Class<?> testedType = null;680 if (isClass(tested)) {681 testedType = (Class<?>) tested;682 } else {683 testedType = tested.getClass();684 }685 Method[] methods = null;686 if (declaringClass == null) {687 methods = getAllMethods(testedType);688 } else {689 methods = declaringClass.getDeclaredMethods();690 }691 Method potentialMethodToInvoke = null;692 for (Method method : methods) {693 if (methodToExecute == null || method.getName().equals(methodToExecute)) {694 Class<?>[] paramTypes = method.getParameterTypes();695 if ((arguments != null && (paramTypes.length == arguments.length))) {696 if (paramTypes.length == 0) {697 potentialMethodToInvoke = method;698 break;699 }700 boolean wrappedMethodFound = true;701 boolean primitiveMethodFound = true;702 if (!checkIfTypesAreSame(paramTypes, arguments)) {703 wrappedMethodFound = false;704 }705 if (!checkIfTypesAreSame(paramTypes, convertArgumentTypesToPrimitive(paramTypes, arguments))) {706 primitiveMethodFound = false;707 }708 if (wrappedMethodFound || primitiveMethodFound) {709 if (potentialMethodToInvoke == null) {710 potentialMethodToInvoke = method;711 } else {712 /*713 * We've already found a method match before, this714 * means that PowerMock cannot determine which715 * method to expect since there are two methods with716 * the same name and the same number of arguments717 * but one is using wrapper types.718 */719 throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", new Method[] { potentialMethodToInvoke, method });720 }721 }722 } else if (isPotentialVarArgsMethod(method, arguments)) {723 if (potentialMethodToInvoke == null) {724 potentialMethodToInvoke = method;725 } else {726 /*727 * We've already found a method match before, this means728 * that PowerMock cannot determine which method to729 * expect since there are two methods with the same name730 * and the same number of arguments but one is using731 * wrapper types.732 */733 throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", new Method[] { potentialMethodToInvoke, method });734 }735 break;736 } else if (arguments != null && (paramTypes.length != arguments.length)) {737 continue;738 }739 }740 }741 WhiteboxImpl.throwExceptionIfMethodWasNotFound(getType(tested), methodToExecute, potentialMethodToInvoke, arguments);742 return potentialMethodToInvoke;743 }744/**745 * Finds and returns a certain constructor. If the constructor couldn't be746 * found this method delegates to747 * {@link Whitebox#throwExceptionIfConstructorWasNotFound(Class, Object...).748 * 749 * @param type The type where the constructor should be located. 750 * @param arguments The arguments passed to the constructor. 751 * @return The found constructor.752 * @throws TooManyConstructorsFoundException If too many constructors matched.753 * @throws ConstructorNotFoundException If no constructor matched. 754 */755 public static Constructor<?> findConstructorOrThrowException(Class<?> type) {756 final Constructor<?>[] declaredConstructors = filterPowerMockConstructor(type.getDeclaredConstructors());757 if (declaredConstructors.length > 1) {758 throwExceptionWhenMultipleConstructorMatchesFound(declaredConstructors);759 }760 return declaredConstructors[0];761 }762 private static Constructor<?>[] filterPowerMockConstructor(Constructor<?>[] declaredConstructors) {763 Set<Constructor<?>> constructors = new HashSet<Constructor<?>>();764 for (Constructor<?> constructor : declaredConstructors) {765 final Class<?>[] parameterTypes = constructor.getParameterTypes();766 if (parameterTypes.length == 1 && parameterTypes[0].getName().equals("org.powermock.core.IndicateReloadClass")) {767 continue;768 } else {769 constructors.add(constructor);770 }771 }772 return constructors.toArray(new Constructor<?>[constructors.size()]);773 }774/**775 * Finds and returns a certain constructor. If the constructor couldn't be776 * found this method delegates to777 * {@link Whitebox#throwExceptionIfConstructorWasNotFound(Class, Object...).778 * 779 * @param type The type where the constructor should be located. 780 * @param arguments The arguments passed to the constructor. 781 * @return The found constructor.782 * @throws TooManyConstructorsFoundException If too many constructors matched.783 * @throws ConstructorNotFoundException If no constructor matched. 784 */785 public static Constructor<?> findUniqueConstructorOrThrowException(Class<?> type, Object... arguments) {786 if (type == null) {787 throw new IllegalArgumentException("Class type cannot be null.");788 }789 Class<?> unmockedType = getUnmockedType(type);790 if ((unmockedType.isLocalClass() || unmockedType.isAnonymousClass() || unmockedType.isMemberClass())791 && !Modifier.isStatic(unmockedType.getModifiers()) && arguments != null) {792 Object[] argumentsForLocalClass = new Object[arguments.length + 1];793 argumentsForLocalClass[0] = unmockedType.getEnclosingClass();794 System.arraycopy(arguments, 0, argumentsForLocalClass, 1, arguments.length);795 arguments = argumentsForLocalClass;796 }797 Constructor<?>[] constructors = unmockedType.getDeclaredConstructors();798 Constructor<?> potentialConstructor = null;799 for (Constructor<?> constructor : constructors) {800 Class<?>[] paramTypes = constructor.getParameterTypes();801 if ((arguments != null && (paramTypes.length == arguments.length))) {802 if (paramTypes.length == 0) {803 potentialConstructor = constructor;804 break;805 }806 boolean wrappedConstructorFound = true;807 boolean primitiveConstructorFound = true;808 if (!checkIfTypesAreSame(paramTypes, arguments)) {809 wrappedConstructorFound = false;810 }811 if (!checkIfTypesAreSame(paramTypes, convertArgumentTypesToPrimitive(paramTypes, arguments))) {812 primitiveConstructorFound = false;813 }814 if (wrappedConstructorFound || primitiveConstructorFound) {815 if (potentialConstructor == null) {816 potentialConstructor = constructor;817 } else {818 /*819 * We've already found a constructor match before, this820 * means that PowerMock cannot determine which method to821 * expect since there are two methods with the same name822 * and the same number of arguments but one is using823 * wrapper types.824 */825 throwExceptionWhenMultipleConstructorMatchesFound(new Constructor<?>[] { potentialConstructor, constructor });826 }827 }828 } else if (isPotentialVarArgsConstructor(constructor, arguments)) {829 if (potentialConstructor == null) {830 potentialConstructor = constructor;831 } else {832 /*833 * We've already found a constructor match before, this834 * means that PowerMock cannot determine which method to835 * expect since there are two methods with the same name and836 * the same number of arguments but one is using wrapper837 * types.838 */839 throwExceptionWhenMultipleConstructorMatchesFound(new Constructor<?>[] { potentialConstructor, constructor });840 }841 break;842 } else if (arguments != null && (paramTypes.length != arguments.length)) {843 continue;844 }845 }846 WhiteboxImpl.throwExceptionIfConstructorWasNotFound(type, potentialConstructor, arguments);847 return potentialConstructor;848 }849 private static Class<?>[] convertArgumentTypesToPrimitive(Class<?>[] paramTypes, Object[] arguments) {850 Class<?>[] types = new Class<?>[arguments.length];851 for (int i = 0; i < arguments.length; i++) {852 Class<?> argumentType = null;853 if (arguments[i] == null) {854 argumentType = paramTypes[i];855 } else {856 argumentType = getType(arguments[i]);857 }858 Class<?> primitiveWrapperType = PrimitiveWrapper.getPrimitiveFromWrapperType(argumentType);859 if (primitiveWrapperType == null) {860 types[i] = argumentType;861 } else {862 types[i] = primitiveWrapperType;863 }864 }865 return types;866 }867 public static void throwExceptionIfMethodWasNotFound(Class<?> type, String methodName, Method methodToMock, Object... arguments) {868 if (methodToMock == null) {869 String methodNameData = "";870 if (methodName != null) {871 methodNameData = "with name '" + methodName + "' ";872 }873 throw new MethodNotFoundException("No method found " + methodNameData + "with parameter types: [ " + getArgumentTypesAsString(arguments)874 + " ] in class " + getUnmockedType(type).getName() + ".");875 }876 }877 public static void throwExceptionIfFieldWasNotFound(Class<?> type, String fieldName, Field field) {878 if (field == null) {879 throw new FieldNotFoundException("No field was found with name '" + fieldName + "' in class " + getUnmockedType(type).getName() + ".");880 }881 }882 static void throwExceptionIfConstructorWasNotFound(Class<?> type, Constructor<?> potentialConstructor, Object... arguments) {883 if (potentialConstructor == null) {884 String message = "No constructor found in class '" + getUnmockedType(type).getName() + "' with " + "parameter types: [ "885 + getArgumentTypesAsString(arguments) + " ].";886 throw new ConstructorNotFoundException(message);887 }888 }889 private static String getArgumentTypesAsString(Object... arguments) {890 StringBuilder argumentsAsString = new StringBuilder();891 final String noParameters = "<none>";892 if (arguments != null && arguments.length != 0) {893 for (int i = 0; i < arguments.length; i++) {894 String argumentName = null;895 Object argument = arguments[i];896 if (argument instanceof Class<?>) {897 argumentName = ((Class<?>) argument).getName();898 } else if (argument instanceof Class<?>[] && arguments.length == 1) {899 Class<?>[] argumentArray = (Class<?>[]) argument;900 if (argumentArray.length > 0) {901 for (int j = 0; j < argumentArray.length; j++) {902 appendArgument(argumentsAsString, j, argumentArray[j] == null ? "null" : getType(argumentArray[j]).getName(),903 argumentArray);904 }905 return argumentsAsString.toString();906 } else {907 argumentName = noParameters;908 }909 } else if (argument == null) {910 argumentName = "null";911 } else {912 argumentName = getType(argument).getName();913 }914 appendArgument(argumentsAsString, i, argumentName, arguments);915 }916 } else {917 argumentsAsString.append("<none>");918 }919 return argumentsAsString.toString();920 }921 private static void appendArgument(StringBuilder argumentsAsString, int index, String argumentName, Object[] arguments) {922 argumentsAsString.append(argumentName);923 if (index != arguments.length - 1) {924 argumentsAsString.append(", ");925 }926 }927 /**928 * Invoke a constructor. Useful for testing classes with a private929 * constructor when PowerMock cannot determine which constructor to invoke.930 * This only happens if you have two constructors with the same number of931 * arguments where one is using primitive data types and the other is using932 * the wrapped counter part. For example:933 * 934 * <pre>935 * public class MyClass {936 * private MyClass(Integer i) {937 * ...938 * } 939 * 940 * private MyClass(int i) {941 * ...942 * }943 * </pre>944 * 945 * This ought to be a really rare case. So for most situation, use946 * {@link #invokeConstructor(Class, Object...)} instead.947 * 948 * 949 * @return The object created after the constructor has been invoked.950 * @throws Exception951 * If an exception occur when invoking the constructor.952 */953 public static <T> T invokeConstructor(Class<T> classThatContainsTheConstructorToTest, Class<?>[] parameterTypes, Object[] arguments)954 throws Exception {955 if (parameterTypes != null && arguments != null) {956 if (parameterTypes.length != arguments.length) {957 throw new IllegalArgumentException("parameterTypes and arguments must have the same length");958 }959 }960 Constructor<T> constructor = null;961 try {962 constructor = classThatContainsTheConstructorToTest.getDeclaredConstructor(parameterTypes);963 } catch (Exception e) {964 throw new ConstructorNotFoundException("Could not lookup the constructor", e);965 }966 return createInstance(constructor, arguments);967 }968 /**969 * Invoke a constructor. Useful for testing classes with a private970 * constructor.971 * 972 * 973 * @return The object created after the constructor has been invoked.974 * @throws Exception975 * If an exception occur when invoking the constructor.976 */977 public static <T> T invokeConstructor(Class<T> classThatContainsTheConstructorToTest, Object... arguments) throws Exception {978 if (classThatContainsTheConstructorToTest == null) {979 throw new IllegalArgumentException("The class should contain the constructor cannot be null.");980 }981 Class<?>[] argumentTypes = null;982 if (arguments == null) {983 argumentTypes = new Class<?>[0];984 } else {985 argumentTypes = new Class<?>[arguments.length];986 for (int i = 0; i < arguments.length; i++) {987 argumentTypes[i] = getType(arguments[i]);988 }989 }990 Constructor<T> constructor = null;991 Constructor<T> potentialContstructorWrapped = null;992 Constructor<T> potentialContstructorPrimitive = null;993 try {994 potentialContstructorWrapped = classThatContainsTheConstructorToTest.getDeclaredConstructor(argumentTypes);995 } catch (Exception e) {996 // Do nothing, we'll try with primitive type next.997 }998 try {999 potentialContstructorPrimitive = classThatContainsTheConstructorToTest.getDeclaredConstructor(PrimitiveWrapper1000 .toPrimitiveType(argumentTypes));1001 } catch (Exception e) {1002 // Do nothing1003 }1004 if (potentialContstructorPrimitive == null && potentialContstructorWrapped == null) {1005 // Check if we can find a matching var args constructor.1006 constructor = getPotentialVarArgsConstructor(classThatContainsTheConstructorToTest, arguments);1007 if (constructor == null) {1008 throw new ConstructorNotFoundException("Failed to find a constructor with parameter types: [" + getArgumentTypesAsString(arguments)1009 + "]");1010 }1011 } else if (potentialContstructorPrimitive == null && potentialContstructorWrapped != null) {1012 constructor = potentialContstructorWrapped;1013 } else if (potentialContstructorPrimitive != null && potentialContstructorWrapped == null) {1014 constructor = potentialContstructorPrimitive;1015 } else if (arguments == null || arguments.length == 0 && potentialContstructorPrimitive != null) {1016 constructor = potentialContstructorPrimitive;1017 } else {1018 throw new TooManyConstructorsFoundException(1019 "Could not determine which constructor to execute. Please specify the parameter types by hand.");1020 }1021 return createInstance(constructor, arguments);1022 }1023 @SuppressWarnings("unchecked")1024 private static <T> Constructor<T> getPotentialVarArgsConstructor(Class<T> classThatContainsTheConstructorToTest, Object... arguments) {1025 if (areAllArgumentsOfSameType(arguments)) {1026 Constructor<T>[] declaredConstructors = (Constructor<T>[]) classThatContainsTheConstructorToTest.getDeclaredConstructors();1027 for (Constructor<T> possibleVarArgsConstructor : declaredConstructors) {1028 if (possibleVarArgsConstructor.isVarArgs()) {1029 if (arguments == null || arguments.length == 0) {1030 return possibleVarArgsConstructor;1031 } else {1032 Class<?>[] parameterTypes = possibleVarArgsConstructor.getParameterTypes();1033 if (parameterTypes[parameterTypes.length - 1].getComponentType().isAssignableFrom(getType(arguments[0]))) {1034 return possibleVarArgsConstructor;1035 }1036 }1037 }1038 }1039 }1040 return null;1041 }1042 private static <T> T createInstance(Constructor<T> constructor, Object... arguments) throws Exception {1043 if (constructor == null) {1044 throw new IllegalArgumentException("Constructor cannot be null");1045 }1046 constructor.setAccessible(true);1047 T createdObject = null;1048 try {1049 if (constructor.isVarArgs()) {1050 Class<?>[] parameterTypes = constructor.getParameterTypes();1051 final int varArgsIndex = parameterTypes.length - 1;1052 Class<?> varArgsType = parameterTypes[varArgsIndex].getComponentType();1053 Object varArgsArrayInstance = createAndPopulateVarArgsArray(varArgsType, varArgsIndex, arguments);1054 Object[] completeArgumentList = new Object[parameterTypes.length];1055 for (int i = 0; i < varArgsIndex; i++) {1056 completeArgumentList[i] = arguments[i];1057 }1058 completeArgumentList[completeArgumentList.length - 1] = varArgsArrayInstance;1059 createdObject = constructor.newInstance(completeArgumentList);1060 } else {1061 createdObject = constructor.newInstance(arguments);1062 }1063 } catch (InvocationTargetException e) {1064 Throwable cause = e.getCause();1065 if (cause instanceof Exception) {1066 throw (Exception) cause;1067 } else if (cause instanceof Error) {1068 throw (Error) cause;1069 }1070 }1071 return createdObject;1072 }1073 private static Object createAndPopulateVarArgsArray(Class<?> varArgsType, int varArgsStartPosition, Object... arguments) {1074 Object arrayInstance = Array.newInstance(varArgsType, arguments.length - varArgsStartPosition);1075 for (int i = varArgsStartPosition; i < arguments.length; i++) {1076 Array.set(arrayInstance, i - varArgsStartPosition, arguments[i]);1077 }1078 return arrayInstance;1079 }1080 /**1081 * Get all methods in a class hierarchy! Both declared an non-declared (no1082 * duplicates).1083 * 1084 * @param clazz1085 * The class whose methods to get.1086 * @return All methods declared in this class hierarchy.1087 */1088 public static Method[] getAllMethods(Class<?> clazz) {1089 if (clazz == null) {1090 throw new IllegalArgumentException("You must specify a class in order to get the methods.");1091 }1092 Set<Method> methods = new LinkedHashSet<Method>();1093 Class<?> thisType = clazz;1094 while (thisType != null) {1095 final Class<?> type = thisType;1096 final Method[] declaredMethods = AccessController.doPrivileged(new PrivilegedAction<Method[]>() {1097 public Method[] run() {1098 return type.getDeclaredMethods();1099 }1100 });1101 for (Method method : declaredMethods) {1102 method.setAccessible(true);1103 methods.add(method);1104 }1105 thisType = thisType.getSuperclass();1106 }1107 return methods.toArray(new Method[0]);1108 }1109 /**1110 * Get all public methods for a class (no duplicates)! Note that the1111 * class-hierarchy will not be traversed.1112 * 1113 * @param clazz1114 * The class whose methods to get.1115 * @return All public methods declared in <tt>this</tt> class.1116 */1117 private static Method[] getAllPublicMethods(Class<?> clazz) {1118 if (clazz == null) {1119 throw new IllegalArgumentException("You must specify a class in order to get the methods.");1120 }1121 Set<Method> methods = new LinkedHashSet<Method>();1122 for (Method method : clazz.getMethods()) {1123 method.setAccessible(true);1124 methods.add(method);1125 }1126 return methods.toArray(new Method[0]);1127 }1128 /**1129 * Get all fields in a class hierarchy! Both declared an non-declared (no1130 * duplicates).1131 * 1132 * @param clazz1133 * The class whose fields to get.1134 * @return All fields declared in this class hierarchy.1135 */1136 public static Field[] getAllFields(Class<?> clazz) {1137 if (clazz == null) {1138 throw new IllegalArgumentException("You must specify the class that contains the fields");1139 }1140 Set<Field> fields = new LinkedHashSet<Field>();1141 Class<?> thisType = clazz;1142 while (thisType != null) {1143 final Field[] declaredFields = thisType.getDeclaredFields();1144 for (Field field : declaredFields) {1145 field.setAccessible(true);1146 fields.add(field);1147 }1148 thisType = thisType.getSuperclass();1149 }1150 return fields.toArray(new Field[fields.size()]);1151 }1152 /**1153 * Get the first parent constructor defined in a super class of1154 * <code>klass</code>.1155 * 1156 * @param klass1157 * The class where the constructor is located. <code>null</code>1158 * ).1159 * @return A <code>java.lang.reflect.Constructor</code>.1160 */1161 public static Constructor<?> getFirstParentConstructor(Class<?> klass) {1162 try {1163 return getUnmockedType(klass).getSuperclass().getDeclaredConstructors()[0];1164 } catch (Exception e) {1165 throw new ConstructorNotFoundException("Failed to lookup constructor.", e);1166 }1167 }1168 /**1169 * Finds and returns a method based on the input parameters. If no1170 * <code>parameterTypes</code> are present the method will return the first1171 * method with name <code>methodNameToMock</code>. If no method was found,1172 * <code>null</code> will be returned. If no <code>methodName</code> is1173 * specified the method will be found based on the parameter types. If1174 * neither method name nor parameters are specified an1175 * {@link IllegalArgumentException} will be thrown.1176 * 1177 * @param <T>1178 * @param type1179 * @param methodName1180 * @param parameterTypes1181 * @return1182 */1183 public static <T> Method findMethod(Class<T> type, String methodName, Class<?>... parameterTypes) {1184 if (methodName == null && parameterTypes == null) {1185 throw new IllegalArgumentException("You must specify a method name or parameter types.");1186 }1187 List<Method> matchingMethodsList = new LinkedList<Method>();1188 for (Method method : getAllMethods(type)) {1189 if (methodName == null || method.getName().equals(methodName)) {1190 if (parameterTypes != null && parameterTypes.length > 0) {1191 // If argument types was supplied, make sure that they1192 // match.1193 Class<?>[] paramTypes = method.getParameterTypes();1194 if (!checkIfTypesAreSame(parameterTypes, paramTypes)) {1195 continue;1196 }1197 }1198 // Add the method to the matching methods list.1199 matchingMethodsList.add(method);1200 }1201 }1202 Method methodToMock = null;1203 if (matchingMethodsList.size() > 0) {1204 if (matchingMethodsList.size() == 1) {1205 // We've found a unique method match.1206 methodToMock = matchingMethodsList.get(0);1207 } else if (parameterTypes.length == 0) {1208 /*1209 * If we've found several matches and we've supplied no1210 * parameter types, go through the list of found methods and see1211 * if we have a method with no parameters. In that case return1212 * that method.1213 */1214 for (Method method : matchingMethodsList) {1215 if (method.getParameterTypes().length == 0) {1216 methodToMock = method;1217 break;1218 }1219 }1220 if (methodToMock == null) {1221 WhiteboxImpl.throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", matchingMethodsList.toArray(new Method[0]));1222 }1223 } else {1224 // We've found several matching methods.1225 WhiteboxImpl.throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", matchingMethodsList.toArray(new Method[0]));1226 }1227 }1228 return methodToMock;1229 }1230 public static boolean isProxy(Class<?> type) {1231 return proxyFramework.isProxy(type);1232 }1233 public static <T> Class<?> getUnmockedType(Class<T> type) {1234 if (type == null) {1235 throw new IllegalArgumentException("type cannot be null");1236 }1237 Class<?> unmockedType = null;1238 if (proxyFramework != null && proxyFramework.isProxy(type)) {1239 unmockedType = proxyFramework.getUnproxiedType(type);1240 } else if (Proxy.isProxyClass(type)) {1241 unmockedType = type.getInterfaces()[0];1242 } else {1243 unmockedType = type;1244 }1245 return unmockedType;1246 }1247 static void throwExceptionWhenMultipleMethodMatchesFound(String helpInfo, Method[] methods) {1248 if (methods == null || methods.length < 2) {1249 throw new IllegalArgumentException("Internal error: throwExceptionWhenMultipleMethodMatchesFound needs at least two methods.");1250 }1251 StringBuilder sb = new StringBuilder();1252 sb.append("Several matching methods found, please specify the ");1253 sb.append(helpInfo);1254 sb.append(" so that PowerMock can determine which method you're refering to.\n");1255 sb.append("Matching methods in class ").append(methods[0].getDeclaringClass().getName()).append(" were:\n");1256 for (Method method : methods) {1257 sb.append(method.getReturnType().getName()).append(" ");1258 sb.append(method.getName()).append("( ");1259 final Class<?>[] parameterTypes = method.getParameterTypes();1260 for (Class<?> paramType : parameterTypes) {1261 sb.append(paramType.getName()).append(".class ");1262 }1263 sb.append(")\n");1264 }1265 throw new TooManyMethodsFoundException(sb.toString());1266 }1267 static void throwExceptionWhenMultipleConstructorMatchesFound(Constructor<?>[] constructors) {1268 if (constructors == null || constructors.length < 2) {1269 throw new IllegalArgumentException("Internal error: throwExceptionWhenMultipleConstructorMatchesFound needs at least two constructors.");1270 }1271 StringBuilder sb = new StringBuilder();1272 sb1273 .append("Several matching constructors found, please specify the argument parameter types so that PowerMock can determine which method you're refering to.\n");1274 sb.append("Matching constructors in class ").append(constructors[0].getDeclaringClass().getName()).append(" were:\n");1275 for (Constructor<?> constructor : constructors) {1276 sb.append(constructor.getName()).append("( ");1277 final Class<?>[] parameterTypes = constructor.getParameterTypes();1278 for (Class<?> paramType : parameterTypes) {1279 sb.append(paramType.getName()).append(".class ");1280 }1281 sb.append(")\n");1282 }1283 throw new TooManyConstructorsFoundException(sb.toString());1284 }1285 @SuppressWarnings("all")1286 public static Method findMethodOrThrowException(Class<?> type, String methodName, Class<?>... parameterTypes) {1287 Method methodToMock = findMethod(type, methodName, parameterTypes);1288 throwExceptionIfMethodWasNotFound(type, methodName, methodToMock, parameterTypes);1289 return methodToMock;1290 }1291 /**1292 * Get an array of {@link Method}'s that matches the supplied list of method1293 * names. Both instance and static methods are taken into account.1294 * 1295 * @param clazz1296 * The class that should contain the methods.1297 * @param methodNames1298 * Names of the methods that will be returned.1299 * @return An array of Method's. May be of length 0 but not1300 * <code>null</code>.1301 * @throws MethodNotFoundException1302 * If no method was found.1303 */1304 public static Method[] getMethods(Class<?> clazz, String... methodNames) {1305 if (methodNames == null || methodNames.length == 0) {1306 throw new IllegalArgumentException("You must supply at least one method name.");1307 }1308 final List<Method> methodsToMock = new LinkedList<Method>();1309 Method[] allMethods = null;1310 if (clazz.isInterface()) {1311 allMethods = getAllPublicMethods(clazz);1312 } else {1313 allMethods = getAllMethods(clazz);1314 }1315 for (Method method : allMethods) {1316 for (String methodName : methodNames) {1317 if (method.getName().equals(methodName)) {1318 method.setAccessible(true);1319 methodsToMock.add(method);1320 }1321 }1322 }1323 final Method[] methodArray = methodsToMock.toArray(new Method[0]);1324 if (methodArray.length == 0) {1325 throw new MethodNotFoundException(String.format("No methods matching the name(s) %s were found in the class hierarchy of %s.",1326 concatenateStrings(methodNames), getType(clazz)));1327 }1328 return methodArray;1329 }1330 /**1331 * Get an array of {@link Field}'s that matches the supplied list of field1332 * names. Both instance and static fields are taken into account.1333 * 1334 * @param clazz1335 * The class that should contain the fields.1336 * @param fieldNames1337 * Names of the fields that will be returned.1338 * @return An array of Field's. May be of length 0 but not <code>null</code>1339 * .1340 */1341 public static Field[] getFields(Class<?> clazz, String... fieldNames) {1342 final List<Field> fields = new LinkedList<Field>();1343 for (Field field : getAllFields(clazz)) {1344 for (String fieldName : fieldNames) {1345 if (field.getName().equals(fieldName)) {1346 fields.add(field);1347 }1348 }1349 }1350 final Field[] fieldArray = fields.toArray(new Field[fields.size()]);1351 if (fieldArray.length == 0) {1352 throw new FieldNotFoundException(String.format("No fields matching the name(s) %s were found in the class hierarchy of %s.",1353 concatenateStrings(fieldNames), getType(clazz)));1354 }1355 return fieldArray;1356 }1357 @SuppressWarnings("unchecked")1358 public static <T> T performMethodInvocation(Object tested, Method methodToInvoke, Object... arguments) throws Exception {1359 final boolean accessible = methodToInvoke.isAccessible();1360 if (!accessible) {1361 methodToInvoke.setAccessible(true);1362 }1363 try {1364 if (isPotentialVarArgsMethod(methodToInvoke, arguments)) {1365 Class<?>[] parameterTypes = methodToInvoke.getParameterTypes();1366 final int varArgsIndex = parameterTypes.length - 1;1367 Class<?> varArgsType = parameterTypes[varArgsIndex].getComponentType();1368 Object varArgsArrayInstance = createAndPopulateVarArgsArray(varArgsType, varArgsIndex, arguments);1369 Object[] completeArgumentList = new Object[parameterTypes.length];1370 for (int i = 0; i < varArgsIndex; i++) {1371 completeArgumentList[i] = arguments[i];1372 }1373 completeArgumentList[completeArgumentList.length - 1] = varArgsArrayInstance;1374 return (T) methodToInvoke.invoke(tested, completeArgumentList);1375 } else {1376 return (T) methodToInvoke.invoke(tested, arguments == null ? new Object[] { arguments } : arguments);1377 }1378 } catch (InvocationTargetException e) {1379 Throwable cause = e.getCause();1380 if (cause instanceof Exception) {1381 throw (Exception) cause;1382 } else if (cause instanceof Error) {1383 throw (Error) cause;1384 } else {1385 throw new MethodInvocationException(cause);1386 }1387 } finally {1388 if (!accessible) {1389 methodToInvoke.setAccessible(false);1390 }1391 }1392 }1393 public static <T> Method[] getAllMethodExcept(Class<T> type, String... methodNames) {1394 List<Method> methodsToMock = new LinkedList<Method>();1395 Method[] methods = getAllMethods(type);1396 iterateMethods: for (Method method : methods) {1397 for (String methodName : methodNames) {1398 if (method.getName().equals(methodName)) {1399 continue iterateMethods;1400 }1401 }1402 methodsToMock.add(method);1403 }1404 return methodsToMock.toArray(new Method[0]);1405 }1406 public static <T> Method[] getAllMetodsExcept(Class<T> type, String methodNameToExclude, Class<?>[] argumentTypes) {1407 Method[] methods = getAllMethods(type);1408 List<Method> methodList = new ArrayList<Method>();1409 outer: for (Method method : methods) {1410 if (method.getName().equals(methodNameToExclude)) {1411 if (argumentTypes != null && argumentTypes.length > 0) {1412 final Class<?>[] args = method.getParameterTypes();1413 if (args != null && args.length == argumentTypes.length) {1414 for (int i = 0; i < args.length; i++) {1415 if (args[i].isAssignableFrom(getUnmockedType(argumentTypes[i]))) {1416 /*1417 * Method was not found thus it should not be1418 * mocked. Continue to investigate the next1419 * method.1420 */1421 continue outer;1422 }1423 }1424 }1425 } else {1426 continue;1427 }1428 }1429 methodList.add(method);1430 }1431 return methodList.toArray(new Method[0]);1432 }1433 public static boolean areAllMethodsStatic(Method... methods) {1434 for (Method method : methods) {1435 if (!Modifier.isStatic(method.getModifiers())) {1436 return false;1437 }1438 }1439 return true;1440 }1441 /**1442 * Check if all arguments are of the same type.1443 */1444 static boolean areAllArgumentsOfSameType(Object[] arguments) {1445 if (arguments == null || arguments.length <= 1) {1446 return true;1447 }1448 // Handle null values1449 int index = 0;1450 Object object = null;1451 while (object == null && index < arguments.length) {1452 object = arguments[index++];1453 }1454 if (object == null) {1455 return true;1456 }1457 // End of handling null values1458 final Class<?> firstArgumentType = getType(object);1459 for (int i = index; i < arguments.length; i++) {1460 final Object argument = arguments[i];1461 if (argument != null && !getType(argument).isAssignableFrom(firstArgumentType)) {1462 return false;1463 }1464 }1465 return true;1466 }1467 /**1468 * @return <code>true</code> if all actual parameter types are assignable1469 * from the expected arguments, <code>false</code> otherwise.1470 */1471 private static boolean checkIfTypesAreSame(Class<?>[] parameterTypes, Object[] arguments) {1472 if (parameterTypes == null) {1473 throw new IllegalArgumentException("parameter types cannot be null");1474 } else if (parameterTypes.length != arguments.length) {1475 return false;1476 }1477 for (int i = 0; i < parameterTypes.length; i++) {1478 Object argument = arguments[i];1479 if (argument == null) {1480 continue;1481 } else {1482 if (!parameterTypes[i].isAssignableFrom(getType(argument)) && !(parameterTypes[i].equals(Class.class) && isClass(argument))) {1483 return false;1484 }1485 }1486 }1487 return true;1488 }1489 /**1490 * @return The type of the of an object.1491 */1492 public static Class<?> getType(Object object) {1493 Class<?> type = null;1494 if (isClass(object)) {1495 type = (Class<?>) object;1496 } else if (object != null) {1497 type = object.getClass();1498 }1499 return getUnmockedType(type);1500 }1501 /**1502 * Get an inner class type1503 * 1504 * @param declaringClass1505 * The class in which the inner class is declared.1506 * @param name1507 * The unqualified name (simple name) of the inner class.1508 * @return The type.1509 */1510 @SuppressWarnings("unchecked")1511 public static Class<Object> getInnerClassType(Class<?> declaringClass, String name) throws ClassNotFoundException {1512 return (Class<Object>) Class.forName(declaringClass.getName() + "$" + name);1513 }1514 /**1515 * Get the type of a local inner class.1516 * 1517 * @param declaringClass1518 * The class in which the local inner class is declared.1519 * @param occurrence1520 * The occurrence of the local class. For example if you have two1521 * local classes in the <code>declaringClass</code> you must pass1522 * in <code>1</code> if you want to get the type for the first1523 * one or <code>2</code> if you want the second one.1524 * @param name1525 * The unqualified name (simple name) of the local class.1526 * @return The type.1527 */1528 @SuppressWarnings("unchecked")1529 public static Class<Object> getLocalClassType(Class<?> declaringClass, int occurrence, String name) throws ClassNotFoundException {1530 return (Class<Object>) Class.forName(declaringClass.getName() + "$" + occurrence + name);1531 }1532 /**1533 * Get the type of an anonymous inner class.1534 * 1535 * @param declaringClass1536 * The class in which the anonymous inner class is declared.1537 * @param occurrence1538 * The occurrence of the anonymous inner class. For example if1539 * you have two anonymous inner classes classes in the1540 * <code>declaringClass</code> you must pass in <code>1</code> if1541 * you want to get the type for the first one or <code>2</code>1542 * if you want the second one.1543 * @return The type.1544 */1545 @SuppressWarnings("unchecked")1546 public static Class<Object> getAnonymousInnerClassType(Class<?> declaringClass, int occurrence) throws ClassNotFoundException {1547 return (Class<Object>) Class.forName(declaringClass.getName() + "$" + occurrence);1548 }1549 /**1550 * Get all fields annotated with a particular annotation. This method1551 * traverses the class hierarchy when checking for the annotation.1552 * 1553 * @param object1554 * The object to look for annotations. Note that if're you're1555 * passing an object only instance fields are checked, passing a1556 * class will only check static fields.1557 * @param annotation1558 * The annotation type to look for.1559 * @param additionalAnnotations1560 * Optionally more annotations to look for. If any of the1561 * annotations are associated with a particular field it will be1562 * added to the resulting <code>Set</code>.1563 * @return A set of all fields containing the particular annotation.1564 */1565 @SuppressWarnings("unchecked")1566 public static Set<Field> getFieldsAnnotatedWith(Object object, Class<? extends Annotation> annotation,1567 Class<? extends Annotation>... additionalAnnotations) {1568 Class<? extends Annotation>[] annotations = null;1569 if (additionalAnnotations == null || additionalAnnotations.length == 0) {1570 annotations = (Class<? extends Annotation>[]) new Class<?>[] { annotation };1571 } else {1572 annotations = (Class<? extends Annotation>[]) new Class<?>[additionalAnnotations.length + 1];1573 annotations[0] = annotation;1574 System.arraycopy(additionalAnnotations, 0, annotations, 1, additionalAnnotations.length);1575 }1576 return getFieldsAnnotatedWith(object, annotations);1577 }1578 /**1579 * Get all fields annotated with a particular annotation. This method1580 * traverses the class hierarchy when checking for the annotation.1581 * 1582 * @param object1583 * The object to look for annotations. Note that if're you're1584 * passing an object only instance fields are checked, passing a1585 * class will only check static fields.1586 * @param annotationTypes1587 * The annotation types to look for1588 * @return A set of all fields containing the particular annotation(s).1589 * @since 1.31590 */1591 public static Set<Field> getFieldsAnnotatedWith(Object object, Class<? extends Annotation>[] annotationTypes) {1592 return findAllFieldsUsingStrategy(new FieldAnnotationMatcherStrategy(annotationTypes), object, true, getType(object));1593 }1594 /**1595 * Get all fields assignable from a particular type. This method traverses1596 * the class hierarchy when checking for the type.1597 * 1598 * @param object1599 * The object to look for type. Note that if're you're passing an1600 * object only instance fields are checked, passing a class will1601 * only check static fields.1602 * @param type1603 * The type to look for.1604 * @return A set of all fields of the particular type.1605 */1606 public static Set<Field> getFieldsOfType(Object object, Class<?> type) {1607 return findAllFieldsUsingStrategy(new AssignableFromFieldTypeMatcherStrategy(type), object, true, getType(object));1608 }1609 /**1610 * Get all instance fields for a particular object. It returns all fields1611 * regardless of the field modifier and regardless of where in the class1612 * hierarchy a field is located.1613 * 1614 * @param object1615 * The object whose instance fields to get.1616 * @return All instance fields in the hierarchy. All fields are set to1617 * accessible1618 */1619 public static Set<Field> getAllInstanceFields(Object object) {1620 return findAllFieldsUsingStrategy(new AllFieldsMatcherStrategy(), object, true, getType(object));1621 }1622 /**1623 * Get all static fields for a particular type.1624 * 1625 * @param type1626 * The class whose static fields to get.1627 * @return All static fields in <code>type</code>. All fields are set to1628 * accessible.1629 */1630 public static Set<Field> getAllStaticFields(Class<?> type) {1631 final Set<Field> fields = new LinkedHashSet<Field>();1632 final Field[] declaredFields = type.getDeclaredFields();1633 for (Field field : declaredFields) {1634 if (Modifier.isStatic(field.getModifiers())) {...

Full Screen

Full Screen

findAllFieldsUsingStrategy

Using AI Code Generation

copy

Full Screen

1List<Field> fields = PowerMockito.method(WhiteboxImpl.class, "findAllFieldsUsingStrategy", Class.class, FieldSearchStrategy.class).withArguments(clazz, FieldSearchStrategy.INHERITANCE).invoke();2Field[] fields = PowerMockito.method(WhiteboxImpl.class, "getFieldsIncludingInherited", Class.class).withArguments(clazz).invoke();3Field[] fields = PowerMockito.method(WhiteboxImpl.class, "getFieldsIncludingInherited", Class.class).withArguments(clazz).invoke();4Field[] fields = PowerMockito.method(WhiteboxImpl.class, "getFieldsIncludingInherited", Class.class).withArguments(clazz).invoke();5Field[] fields = PowerMockito.method(WhiteboxImpl.class, "getFieldsIncludingInherited", Class.class).withArguments(clazz).invoke();6Field[] fields = PowerMockito.method(WhiteboxImpl.class, "getFieldsIncludingInherited", Class.class).withArguments(clazz).invoke();

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Powermock automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used method in WhiteboxImpl

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful