Hello I'm testing the class that has some validating methods and I've been wondering if there is a way to reduce the duplicated code.
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
for (String email : correctEmails) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
System.out.println("Email is valid: " + email + ": " + isValid);
assertTrue(isValid);
}
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
for(String username : correctUsernames) {
boolean isValid = UserCredentialsValidator.usernameValidation(username, userList);
System.out.println("Username is valid: " + username + " : " + isValid);
assertTrue(isValid);
}
}
I also have validators for other fields such as username etc. I was thinking about implementing a helper method that would accept: tested credential as String, List but I've got a problem with last parameter - a validating method, not sure how to pass that.
The code i would like to replace with some method is the for loop.
I am afraid your tests are of low quality.
The problems that should be fixed immediately include
UserCredentialsValidator.usernameValidation(username, userList); The method shouldn't take the second argument. The place from where that list is retrieved should be concealed from the API consumer.
List<String> correctEmails = Arrays.asList(...) and List<String> correctUsernames = Arrays.asList(...) should be removed. You'd better make the tests parameterised with #ParameterizedTest and #ValueSource.
I'd rather remove the System.out.println statements. They make little sense in tests.
#ParameterizedTest
#ValueSource(strings = {"test#test.com", "test123#test123.com"})
void testUserEmailValidationWithValidUserEmailShouldPass(String validUserEmail) {
boolean isValid = UserCredentialsValidator.emailValidator(validUserEmail);
assertTrue(isValid);
}
#ParameterizedTest
#ValueSource(strings = {"username", "123username"})
void testUserNameValidationWithValidUserNameShouldPass(String validUserName) {
boolean isValid = UserCredentialsValidator.usernameValidation(validUserName);
assertTrue(isValid);
}
Now there is nothing to reduce.
As I already stated in my comment to your question, I'm not sure rearranging your code would help much. However, as a comparision here's a Java8+ version which uses a common method:
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
testValidator( "Email", correctEmails , email -> UserCredentialsValidator.emailValidator(email) );
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
//I don't know where userList does come from but it would need to be final to be used here
testValidator( "Username", correctUsernames, username -> UserCredentialsValidator.usernameValidation(username, userList) );
}
void testValidator( String name, List<String> data, Predicate<String> validator) {
for( String element : data ) {
boolean isValid = validator.test( element );
System.out.println( name + " is valid: " + element + " : " + isValid);
assertTrue(isValid);
}
}
In that particular case both approaches would be 23 lines long while the second one might be easier to reuse but harder to understand and less flexible (e.g. if you'd need to pass additional parameters etc.)
Use parameterized tests:
static Stream<String> emailsSource() {
return Stream.of("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
}
#Test
#MethodSource("emailsSource")
void testCorrectEmailValidator(String email) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
assertTrue(isValid);
}
Repeat for usernameSource etc. IMHO, this is sufficient to eliminate duplicities.
However if you want to go further and generalize it, use method references. I wouldn't recommend it though.
static Stream<Pair<String,Predicate<String>>> allSources() {
return Stream.of(
Pair.of("test#test.com", UserCredentialsValidator::emailValidator),
Pair.of("username", UserCredentialsValidator::usernameValidationOneArg), // like usernameValidation but with argument userList fixed
...
);
}
#Test
#MethodSource("allSources")
void testAll(Pair<String,Predicate<String>> p) {
String s = p.getLeft();
Predicate<String> test = p.getRight();
boolean isValid = test.apply(email);
assertTrue(isValid);
}
Fact that you're struggling to test is indicating a design smell.
Its good time for you to explore strategy design pattern here.
Basically you main code would look something like
interface IValidator {
boolean isValid(List<String> yourDataToBeValidated);
}
Now create multiple validator classes for different fields like email, username etc.
class EmailValidator implements IValidator {
boolean isValid(List<String> yourDataToBeValidated){
//Email specific validations goes here
}
}
You can create more validators as you need on the go.
Now in your unit tests create new EmailValidator() or new UsernameValidator() and pass your emailIds or usernames to be isValid() method, something like below :
boolean isValid = new EmailValidator().isValid(Arrays.asList("test#test.com", "test123#test123.com");
assertTrue(isValid);
Related
First off: I absolutely LOVE Project Lombok. Awesome tool! There's so many excellent aspects to this 'compile time' library.
Loving the #ExtensionMethods, I have already hit this 'feature' a few times, so now it's time for me to ask this question:
Suppose I have the following classes:
#UtilityClass
public class AObject {
static public String message(final Object pObject) {
return "AObject = " + (pObject != null);
}
}
#UtilityClass
public class AString {
static public String message(final String pObject) {
return "AString = " + (pObject != null);
}
}
#ExtensionMethod({ AObject.class, AString.class })
public class Run_Object_String {
public static void main(final String[] args) {
System.out.println("\nRun_Object_String.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
#ExtensionMethod({ AString.class, AObject.class })
public class Run_String_Object {
public static void main(final String[] args) {
System.out.println("\nRun_String_Object.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
public class ClassPrevalenceTest {
public static void main(final String[] args) {
Run_Object_String.main(args);
Run_String_Object.main(args);
}
}
With the output:
Run_Object_String.main()
Testing s: AObject = true
Testing o: AObject = true
Testing s: AObject = true
Run_String_Object.main()
Testing s: AString = true
Testing o: AObject = true
Testing s: AString = true
Why is this?
Why is the message(String) not called in the first example, even though it has a better method signature fit than message(Object)?
Why is #ExtensionMethod dependent on sequence of the arguments?
Here's what I blindly assume:
when parsing for ExtensionMethods, Lombok will process annotation values from left to right
For Run_Object_String that means: first AObject, then AString
For Run_String_Object that means: first AString, then AObject
Object-String: When patching AObject into class Run_Object_String, the message(Object) method will be added. And when patching in AString with the message(String) method, it will not be added.
Presumably because the message(Object) also matches a call to message(String), so message(String) will not be added.
String-Object: When patching AString into class Run_String_Object, the message(String) method will be added.
When patching in AObject class with message(Object), the old and present message(String) method will NOT accept the call message(Object), thus the method message(Object) will be added.
So, apart from taking great care of what order I add the #UtilityClass references, are there any other solutions to this?
Can the Lombok preprocessor be extended and made more sensible when adding in extension methods?
Do you guys have any suggestions regarding this, or an explanation of what is really happening (as opposed to my assumptions)
This is a fascinating use of Lombok I wasn't aware of. The best place I think you could delve to find your answers is the source itself since the docs on this experimental work seems pretty light, understandably.
Take a look on git here: HandleExtensionMethod.
I am guessing based on the logic that the area that's effectively "fitting" the right method from the annotation is as below..
Instead of trying for a "best" fit, it seems to be aiming for a "first" fit.
That is, it appears to iterate over List<Extension> extensions. Since it's a Java list, we assume ordering is preserved in the order the extensions were specified in the original annotation.
It appears to simply work in order of the list and return as soon as something matches the right method and type shape.
Types types = Types.instance(annotationNode.getContext());
for (Extension extension : extensions) {
TypeSymbol extensionProvider = extension.extensionProvider;
if (surroundingTypeSymbol == extensionProvider) continue;
for (MethodSymbol extensionMethod : extension.extensionMethods) {
if (!methodName.equals(extensionMethod.name.toString())) continue;
Type extensionMethodType = extensionMethod.type;
if (!MethodType.class.isInstance(extensionMethodType) && !ForAll.class.isInstance(extensionMethodType)) continue;
Type firstArgType = types.erasure(extensionMethodType.asMethodType().argtypes.get(0));
if (!types.isAssignable(receiverType, firstArgType)) continue;
methodCall.args = methodCall.args.prepend(receiver);
methodCall.meth = chainDotsString(annotationNode, extensionProvider.toString() + "." + methodName);
recursiveSetGeneratedBy(methodCall.meth, methodCallNode);
return;
}
}
You can look at the rest of the code for other insight as there doesn't seem to be too much there (i.e. number of lines) to look at, though admittedly it's an impressive enough a feat to do in that space.
is it possible to create a class and have a String ... attribute that takes as many or as little strings as you put?
example:
please excuse my rough pseudocode, this is for java.
//this is the method:
public void getXXXX(String ...) {
//random code executes in a loop with as many as strings that are inputted
}
//this code calls it
getXXXX("Benjamin","Jordan","Steve")
getXXXX("Pengu","No")
getXXXX("hi")
Yes, what you entered will more or less work, you just need a parameter name after your type.
class StringDecorator {
public static String join(final String... strings) {
final var builder = new StringBuilder();
for (final var string : strings) {
builder.append(string);
}
return builder.toString();
}
}
Then invoke this somewhere
StringDecorator.join("Hello, ", "World!"); // "Hello, World!"
I'm invoking some method of Class's instance using the method.invoke(instance, args...) way but for each method inside the instance, as the invoke Javadoc rightly points out, each argument must be manually specified.
Thinking about Spring... how it could valorize parameters in controller's method behind the hood during HTTP calls? (but surely it does in a completely different way I think...)
I wonder if there's any way in Java to dynamically pass parameters in reflection (or not even reflection) without specifying each of them singularly.
EDIT
The instance class declaration is something like:
public class Something {
public void doSth(String par1, String par2, Integer par3) {
//....
}
public void doSthElse(String par1, Boolean par2) {
//....
}
public void doSthElseMore(Integer par1) {
//....
}
}
How I'm invoking each method:
...
for (Method method : instance.getDeclaredMethods()) {
Object[] array = //BL: build array of values to pass to the invoke method.
//1. doSth may be new Object[] {"abc", "def", 123}
//2. doSthElse iteration may be new Object[] {"abc", false}
//3. doSthElseMore iteration may be new Object[] {123}
return method.invoke(instance, array);
}
...
As shown above, each method inside Something class (instance) have a different number of parameters.
On each iteration, the array have a different number of values to pass to the invoke.
Actually as #Boris says all I had to do to complete my job was to convert each parameters to the correct type. In this way Java managed to invoke the correct method of the Something class with the correct parameters types.
My project is a Vert.x application using Vavr and jodd but the last return statement shows how I managed to solve.
public Object invokeMethod(Object service, Method method, RoutingContext routingContext) throws Exception {
MultiMap queryParams = routingContext.queryParams();
Map<String, String> pathParams = routingContext.pathParams();
Buffer body = routingContext.getBody();
// 1. type, 2. name, 3. value
List<Tuple3<Class<?>, String, Object>> list = List.empty();
for (Parameter par : method.getParameters()) {
ParamQuery paramQuery = par.getAnnotation(ParamQuery.class);
if (paramQuery != null) {
list = list.push(new Tuple3<Class<?>, String, Object>(par.getType(), paramQuery.value(),
queryParams.get(paramQuery.value())));
}
}
// TypeConverterManager used to "covnert" each object (String) from the HTTP call to the correct data type
return method.invoke(service, list.reverse()
.map(mapper -> TypeConverterManager.lookup(mapper._1()).convert(mapper._3())).toJavaArray());
}
However, this project can be found on GitHub
Since I notice you are using an Integer instead of a int (so no primitives parameters in your examples), you can send null to all your methods without any problems.
So you can create an array of the correct length and this will work in your case.
public static Object[] getParametersArray(Parameter[] param){
Object[] array = new Object[param.length];
// create default primitive values based on param[#].getType()
return array;
}
Then, all you have to do is to iterate the method:
Labo l = new Labo();
for(Method m : Labo.class.getDeclaredMethods()){
if((m.getModifiers() & Modifier.STATIC) > 0){
System.out.println("SKIP " + m.getName());
continue;
}
try {
m.invoke(l, getParametersArray(m.getParameters()));
} catch (Exception e) {
e.printStackTrace();
}
}
Notice the skipped static method, mostly because if you run this in the method containing the main method, you will have a recursive call.
This was tested with :
public void test(String s){
System.out.println("test String " + s);
}
public void test2(String s1, String s2){
System.out.println("test String " + s1 + " | String " + s2);
}
public void test(Integer s){
System.out.println("test Integer " + s);
}
SKIP main
test String null
test Integer null
SKIP getParametersArray
test String null | String null
Note : If you need to manage some primitive values, you will need to get the type of the parameter to provide a default value instead of null
I have a code snippet similar to the one below,
public ArrayList getReport(reportJDOList,accountType)
{
String abc = "";
for(ReportJDO reportJDO : reportJDOList)
{
if(accountType.equals("something")
abc = reportJDO.getThis();
else
abc = reportJDO.getThat();
//somecode goes here
}
returning List;
}
As I know the value of accountType before the iteration, I dont want this check to happen, for every entry in a list as it would cause numerous number of checks if the size of reportJDOList is 10000 for an instance. How we remove this thing from happening? Thanks in Advance :)
You can indeed peform check once and implement 2 loops:
if(accountType.equals("something") {
for(ReportJDO reportJDO : reportJDOList) {
abc = reportJDO.getThis();
}
} else {
for(ReportJDO reportJDO : reportJDOList) {
abc = reportJDO.getThat();
}
}
Obviously you can improve your design by either
separating you loops into 2 different methods
Using command pattern, i.e. implementing loop body in different command and executing it to loop.
Using Guava's Function (it is just improvement of #2)
Using java 8 streams.
IF you want to save the String comparison, make it once before the loop and store the result in a boolean variable :
String abc = "";
boolean isThis = accountType.equals("something");
for(ReportJDO reportJDO : reportJDOList) {
abc = isThis ? reportJDO.getThis() : reportJDO.getThat();
//somecode goes here
}
I'd vote for clean coding this - perform the check once and delegate the logic into private methods, each performing the loop individually. This duplicates code for the loop but gives greatest flexibility if at some point you need to do something more in SomethingReport that's not duplicated in OtherReport.
public ArrayList getReport(reportJDOList,accountType) {
if("soemthing".equals(accountType)) {
return getSomethingReport(reportJDOList);
} else {
return getOtherReport(reportJDOList);
}
}
private ArrayList getSomethingReport(reportJDOList) {
[...]
}
interface AccountHandler {
String get(Report r);
}
AccountHandler thisHandler= new AccountHandler() {
#Override
public String get(Report r) {
return r.getThis();
}
};
AccountHandler thatHandler= new AccountHandler() {
#Override
public String get(Report r) {
return r.getThat();
}
};
//...............
AccountHandler ah;
ah = (what.equalsIgnoreCase("this")) ? thisHandler : thatHandler;
Report r=new Report();
// loop
ah.get(r);
//Using reflection:
Report r = new Report();
Method thisMethod = r.getClass().getDeclaredMethod("getThis");
Method thatMethod = r.getClass().getDeclaredMethod("getThat");
Method m = (what.equalsIgnoreCase("this")) ? thisMethod : thatMethod;
m.invoke(r);
I have four different classes classA, classB, classC and classD. All the four classes have the same static method search() which takes two string parameters. If i want to invoke static method search in four different classes from main class at once. How can I do that. For now my code is as follows for main class. I need to execute the same thing for other 3 classes also. How can i do that and display the results of other 3 in the same way as for classA. The way search is done in 4 classes r different but they should give the same result.
Main() {
Object[] zy;
for (String pattern : Read.arrayList) {
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern + " match is found at index : "+ results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
I strongly recommend you change this to non-static methods. Look how easy and nice is when you will seperate an interface:
public interface Common {
List<Integer> findAll(String pattern, String dataToSearch);
}
public class A implements Common ...
public class B implements Common ...
public class C implements Common ...
public class D implements Common ...
// in main:
List<Common> allYourClasses = new ArrayList<Common>();
allYourClasses.add(new A());
allYourClasses.add(new B());
allYourClasses.add(new C());
allYourClasses.add(new D());
List<Integer> result = new ArrayList<Integer>();
for (Common c : allYourClasses) {
result.addAll(c.findAll(pattern, dataToSearch));
}
1 - You should NOT do this. Avoid static methods. One of the reason being they can not be called without the exact class. A group of classes that implement a simple interfaces will work faster, safer and better in every way
2 - You can (but you shouldn't) do something like this:
for (Class<?> clazz : new Class[] { ClassA.class, ClassB.class,
ClassC.class }) {
Object[] zy = null;
String dataToSearch = "";
String[] arrayList = { "a" };
for (String pattern : arrayList) {
List<Integer> results = findAllForClass(clazz, pattern,
dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern
+ " match is found at index : " + results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
#SuppressWarnings("unchecked")
public static List<Integer> findAllForClass(Class<?> clazz, String pattern,
String dataToSearch) {
List<Integer> list = null;
try {
list = (List<Integer>) clazz.getDeclaredMethod("findAll", String.class,
String.class).invoke(null, pattern, dataToSearch);
} catch (Exception e) {
list = Collections.emptyList();
}
return list;
}
You see the #supresswarning and the try/catch? well, this is a hint: is telling you you this code is at least suspicious. It is in fact unsafe, non well performant, and is a stupid workaround.
(But we all did something like that once in our lives)
I can't really figure out why would anyone do that.
That said, you could have a method taking a Class as a parameter and calling the method explicitly by name (getMethod.../invoke()).
That puts you back in non static world and you can iterate over the classes you want to invoke. (But again, why use statics in the first place?)
Pseudo untested code:
public void invokeStatic(Class clazz, String method, Class<?> paramsTypes[], Object[] params) {
Method method = clazz.getMethod(method, paramsType);
method.invoke(params);
}
If you want to group all of the results together, just keep adding results to your list:
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
results.addAll(ClassB.findAll(pattern, dataToSearch));
// etc.