Object creation optimisation - java

I have a below piece of code :
Test class is a POJO of (id and name)
Test test=new Test();
Test test1=new Test();
Test test2=new Test();
for(Asset a : assets)
{
assetId=getAssetId();
test.set(assetId);
test1.set(assetId);
test2.set(assetId);
for(some loop)
{
test.set(assetName);
test1.set(assetName1);
test2.set(assetName2);
setMethod(test);
setMethod1(test1);
setMethod2(test2);
}
}
as we can see there's too much repetitive code. can anyone please help to optimise it?

You can refactor your code introducing new methods helping readability and manuteneability of the code.
First define a method to generate a List of Test
private List<Test> createTests(int size) {
List<Test> tests = new ArrayList();
for (int i = 0; i < size; i++) {
tests.add(new Test());
}
return tests;
}
Add a method to set the assetId on each test of a list.
private void setAssetId(List<Test> tests, String assetId) {
for (Test test: tests) {
test.set(assetId);
}
}
Than you can refactoring the first part of code as
List<Test> tests = createTests(3);
for (Asset asset: assets) {
setAssetId(tests, asset.getAssetId());
...
}
The inner loop is not clear (it is more a pseudo code, then java code), but the idea is the same.
If you see some duplication in your code extract a method and refactor the original code calling that method. The whole code will be a lot more readable and if repetitions are many can be also smaller.

With Lambda you can save more code.
Replace the syso with your stuff to do.
package main.java.stackoverflow.oopTest;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class Test {
private int id;
private String name;
public Test(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Test(int id) {
this(id, "");
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public static void main(String[] args) {
List<Test> listOfTest = new ArrayList<>();
// create your POJOS
IntStream.range(0, 3).forEach(index -> {
listOfTest.add(new Test(index));
});
//iterate over your POJOS
listOfTest.forEach(t -> System.out.println(t.id));
}
}

Related

ParametrizedTest with displayName and Argument

I am trying to migrate from JUnit4 to JUnit5 and also I'm new to ParametrizedTest in Junit5 and I have a scenario wherein I would like to provide different DisplayName and the Test argument(Object).
Here's the data source I would like to use as an input for #MethodSource("data")
public static Collection<Object[]> data() throws IOException {
List<Object[]> testCaseData = new ArrayList<>();
TestCaseReader testCaseReader = new TestCaseReader(TESTCASE_CSV_RESOURCE);
List<MyClass> testCaseList = testCaseReader.readTestCases();
for (MyClass testCase : testCaseList) {
if (testCase.isActive()) {
Object[] testParameter = new Object[2];
testParameter[0] = String.format("%03d: %s", testCase.getStartingLineNumber(), testCase.getName());
testParameter[1] = testCase;
testCaseData.add(testParameter);
}
}
return testCaseData;
}
And this is the Test
#ParameterizedTest(name = "Scenario: {0}, testCase={1}")
#MethodSource("data")
public void process(MyClass testCase) {
//...
//some operating on testCase methods/variables
}
When executing TestCase, I see the DisplayName is picked up correctly, but the other arguments is not resolvable it says
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.sample.MyClass testCase] in method [public void.MultipleTestCase.process(com.sample.MyClass testCase)]
Could you please guide me what I have done wrong here!
Thanks
Providing test data as Collection<Object[]> is no longer the appropriate way in JUnit 5. You can use a Stream instead. If you need to provide multiple parameters for your test you can use Arguments to wrap them. They are automatically unwrapped upon test execution. The example below gives a general idea on how to do that. You can replace TestCase with MyClass and insert your TestCaseReader code in data.
public class ParameterizedTest {
static Stream<Arguments> data() {
// TODO: Add your TestCaseReader usage to create MyClass / TestCase instances.
List<TestCase> testCases =
List.of(new TestCase("test01", "data01"), new TestCase("test02", "data02"));
return testCases.stream().map(test -> Arguments.arguments(test.getName(), test));
}
#org.junit.jupiter.params.ParameterizedTest(name = "Scenario: {0}, testCase={1}")
#MethodSource("data")
public void process(String name, TestCase testCase) {
System.out.println(name + ": " + testCase.getData());
// TODO: Work with your test case.
}
private static final class TestCase {
private final String name;
private final String data;
public TestCase(String name, String data) {
this.name = name;
this.data = data;
}
public String getName() {
return name;
}
public String getData() {
return data;
}
}
}

How to mock enum.values() in mockito

First of all i'm learning java and mockito, did search and cannot find proper answer yet.
The pseudo code is like this
public enum ProdEnum {
PROD1(1, "prod1"),
PROD2(2, "prod2"),
......
PROD99(2, "prod2");
private final int id;
private final String name;
private ProdEnum(int id, String name) {
this.id = id;
this.name = name;
}
prublic String getName() { return this.name; }
}
public class enum1 {
public static void main(String[] args) {
// Prints "Hello, World" in the terminal window.
System.out.println("Hello, World");
List<String> prodNames = Array.stream(ProdEnum.values())
.map(prodEnum::getName)
.collect(Collectors.toList());
// verify(prodNames);
}
}
My question is in unit test, how to generate mocked prodNames ?
Only 2 or 3 of the products needed for testing,
In my unit test i tried this
List<ProdEnum> newProds = Arrays.asList(ProdEnum.PROD1, ProdEnum.PROD2);
when(ProdEnum.values()).thenReturn(newProds);
but it says Cannot resolve method 'thenReturn(java.util.List<...ProdEnum>)'
Thanks !
You cannot mock statics in vanilla Mockito.
If your up for a little refactor then:
1) Move enum.values() call into a package level method:
..
List<String> prodNames = Array.stream(getProdNames())
.map(prodEnum::getName)
.collect(Collectors.toList());
..
List<String> getProdNames(){
return ProdEnum.values();
}
2) Spy on your SUT:
enum1 enumOneSpy = Mockito.spy(new enum1());
3) Mock the getProdNames() method:
doReturn(newProds).when(enumOneSpy).getProdNames();

In which way I can write down for-loop with List in Java?

Is it possible to use Lambda expression or better way to write down the for-loop?
public TaskDTO convertToDTO(Task task) {
for (int i = 0; i < task.getPrecedingTasks().size(); i++)
this.precedingTasks.add(task.getPrecedingTasks().get(i).getName());
}
Your solution is good as:
task.getPrecedingTasks().stream().map(Task::getName).forEach(this.precedingTasks::add);
But since you are just retrieving the part of the Task, map and then collect as a list as:
this.precedingTasks = task.getPrecedingTasks().stream().map(Task::getName).collect(Collectors.toList());
Isn't it more straightforward and easier to understand? Since stream here is to do the mapping/converting and then collecting.
And also in this way, you don't need to do the initialisation for the this.precedingTasks as
this.precedingTasks = new ArrayList<>(); // to ensure it's not null;
Anyway, just personal preference here.
This is a complete example, where I put System.out.println ... you should use a this.precedingTasks.addAll( ...
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
Task t1 = new Task("myTask", Arrays.asList(new Task("innerTask1"), new Task("innerTask2")));
System.out.println(t1.precedingTasks.stream().map(Task::getName).collect(Collectors.toList()));
}
static class Task {
private String name;
private List<Task> precedingTasks = new ArrayList<>();
public Task(String name) {
this.name = name;
}
public Task(String name, List<Task> precedingTasks) {
this.name = name;
this.precedingTasks = precedingTasks;
}
public String getName() {
return name;
}
public List<Task> getPrecedingTasks() {
return precedingTasks;
}
}
}
The output is
[innerTask1, innerTask2]
I found the correct solution in my case:
task.getPrecedingTasks().stream().map(Task::getName).forEach(this.precedingTasks::add);
Thanks, for tips : )

Testing builder pattern in Java

We use the builder pattern extensively in our code base, with built objects all having a toBuilder() method. I want to write a unit test that ensures that no fields have been forgotten in the toBuilder() methods, i.e., for any buildable object, I want to a test roughly like this
MyClass obj = getTestObjectWithRandomData();
assertEquals(obj, obj.toBuilder().build());
Now, I can fairly easy write a basic version of getTestObjectWithRandomData() that uses reflection to assign a bunch of values to the fields of any object. However, the snag is that build() often contains tons of validation checks that will throw exceptions if, for example, a certain integer isn't within a sane range. Writing a generalized version of getTestObjectWithRandomData() that conforms all those class-specific validation checks would be impossible.
So, how can I do what I want to do? I'm tempted to segregate the construction and validation code into different methods so that the test doesn't trip on the validation, but then that means that people have to remember to call validate() or whatever on objects after they create them. Not good.
Any other ideas?
How about using Lombok? Would that be an option for you? It will auto-generate the builder code and you'll never again have to worry about it.
https://projectlombok.org/features/Builder
Simply annotate your classes with #Builder
With Lombok
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
#Builder
public class BuilderExample {
private String name;
private int age;
#Singular private Set<String> occupations;
}
Vanilla Java
import java.util.Set;
public class BuilderExample {
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
// go to https://projectlombok.org/features/Singular-snippet.html to see it.
Set<String> occupations = ...;
return new BuilderExample(name, age, occupations);
}
#java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}

Run multiple tests with Selenium DataProvider

I am currently using testng + selenium to automate my tests, and I have the following scenario:
I need to read from an excel file, transform each row in an object and run 1 test for each of them. I am trying to use annotation #DataProvider to return an Array of objects, however it is only able to return Iterators and Objects[][]. Is there any workaround I can use to return an array of Cliente objects from the DataProvider? I have tried the following code, however it only prints the data from Client2:
public class TestDataProvider
{
Cliente cliente;
#DataProvider(name = "test1")
public static Object[][] dataMethod() {
return new Object[][] { { new Cliente("Client1", "1111111111") },
{ new Cliente("Client2", "2222222222") }};
}
#Test(dataProvider = "test1")
public void testMethod(Cliente cliente) {
System.out.println(cliente.getNome() + " " + cliente.getCartao());
}
}
Edit1: Cliente class:
public class Cliente {
private static String name;
private static String card;
//Construtor method
public Cliente(String name, String card){
setname(name);
setCartao(card);
}
public String getName() {
return name;
}
public void setName(String name) {
Cliente.name = name;
}
public String getCard() {
return card;
}
public void setCard(String card) {
Cliente.card = card;
}
}
Values that are printed in the console:
Client2 2222222222
Client2 2222222222
So...
Your prerequisites:
excel file, each row - one dataset
run test for each dataset
What can you do:
Create #DataProvider which return Iterator<Object[]> with your datasets where each Object[] is your row from excel. (the easiest one)
Use #Factory to manually iterate through your datasets and call test methods.
Use #DataProvider to provide data for #Factory and do as above.
The 2nd and 3rd options are complicated, but have some benefits if you has other parameters, except datasets, to run tests.
thanks for the help. With RocketRacoon's third suggestion I managed to resolve my problem. Below is a simple example:
public class ProvidedTest
{
private static nome;
private static cpf;
private static cartao;
#DataProvider
public static Object[][] dataProviderMethod() {
return new Object[][] { {"Client1", "111111111", "444444444"},
{"Client2", "222222222", "555555555"},
{"Client3", "333333333", "666666666"}};
}
#Factory (dataProvider="dataProviderMethod")
public ProvidedTest(String nome, String cpf, String cartao){
this.nome = nome;
this.cpf = cpf;
this.cartao = cartao;
}
#Test
public void testCase(){
System.out.println(cliente.getNome());
System.out.println(cliente.getCpf());
System.out.println(cliente.getCartao());
}
}

Categories

Resources