I have a method in my service layer:
public List<String> sortBarcodesByTotalPrice(Set<String> barcodesByBookType) {
List<String> toSort = new ArrayList<>();
for (String barcode : barcodesByBookType) {
[1] String combined = barcode + "/" + priceOperations.calculatePriceByBarcode(barcode);
toSort.add(combined);
}
[2] toSort.sort(new TotalPriceComparator());
return toSort;
}
[1] this line gets barcode, lets say "ABC" and then calls a method in another class which retrieves information from the database about that item, performs calculation and returns the price.
The possible combined value could be something like "ABC/10".
[2] sorts all the values by price, ascending. So if List contains and "DEF/15" and "ABC/10" it gets sorted to [0] = "ABC/10", [1]= "DEF/15".
When I try to test this method, I get null pointer exception when priceOperations.calculatePRiceByBarcode(barode) gets called. How do I test this method? I've tried searching and found some questions and answers about using mockito but I can't figure out how to implement this.
my current attempt at testing this method:
#Test
void sortBarcodesByTotalPrice() {
Set<String> toSort = new HashSet<>();
toSort.add("ABC/10");
toSort.add("DEF/5");
toSort.add("GHI/15");
List<String> sorted = bookService.sortBarcodesByTotalPrice(toSort);
assertEquals(sorted.get(0), "GHI");
assertEquals(sorted.get(1), "ABC");
assertEquals(sorted.get(2), "DEF");
}
EDIT, SOLUTION:
I have figured out how to solve this thanks to information from Kaj Hejer. This is my new test method, passing and working as intended, if I understand this correctly:
#Test
void sortBarcodesByTotalPrice() {
PriceOperations priceOperations = Mockito.mock(PriceOperations.class);
Set<String> toSort = new HashSet<>();
toSort.add("GHI");
toSort.add("ABC");
toSort.add("DEF");
when(priceOperations.calculatePriceByBarcode("GHI")).thenReturn(new BigDecimal("15"));
when(priceOperations.calculatePriceByBarcode("ABC")).thenReturn(new BigDecimal("10"));
when(priceOperations.calculatePriceByBarcode("DEF")).thenReturn(new BigDecimal("5"));
BookService bookService = new BookService(null, null, priceOperations);
List<String> sorted = bookService.sortBarcodesByTotalPrice(toSort);
assertEquals(sorted.get(0), "DEF/5");
assertEquals(sorted.get(1), "ABC/10");
assertEquals(sorted.get(2), "GHI/15");
}
First you have to decide what to test where. You might want to have separate tests for BookService and for the PriceOperations class. Now your test are tesing both these classes.
If you want to write a test for the BookService class you can mock the PriceOperations class.
List<String> sorted = new ArrayList<>();
sorted.add("GHI");
sorted.add("ABC");
sorted.add("DEF");
PriceOperations priceOperations = Mockito.mock(PriceOperations.class);
when(priceOperations. calculatePriceByBarcode(Mockito.<String>anyList())).thenReturn(sorted);
When injecting the PriceOperations into the BookService constructor injection is a smart way of injecting because it make it easier to write tests. Then you can inject the mocked priceOperations with
BookService bookService = new BookService(priceOperations);
Please let me know if this is useful!
Related
I am using Junit4 and Mockito to test some logic.
After I run the test method, the result is returning an empty list of an object while I have mocked objects and add to the list. It should have one object in the result list.
I have tried to debug the test and still see that the result list does not contain any object. The following code is just to simulate the real code that I have but they are basically the same idea.
This is the method that I want to test: a new list is created inside the method and then there are some filter going on to add items in the list and then return the result.
public List<TemplateDto> getTemplates(String name) {
List<TemplateDto> result = new ArrayList<>();
result.addAll(
template.getTemplates().stream().filter(t -> t.getName().equals(name))
.map(s -> new TemplateDto(s.getId(),s.getName()))
.collect(Collectors.toList())
);
return result;
}
This is the test method logic. I mocked one object, expecting the result to return the same object
#Test
public void getTemplates() {
classToTest = mock(ClassToTest.class);
Template template1 = new Template(1,"template1");
List<Template> templates = new ArrayList<>();
templates.add(template1);
template = mock(Template.class);
when(template.getTemplates()).thenReturn(templates);
List<TemplateDto> result = classToTest.getTemplates("template1");
assertEquals(result.get(0).getName(),"template1");
}
The test should pass but instead, it fails with the following error:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
Is there anything else that I need to mock to get the expected result?
1) You never mock class under test
2) You have to set the mocked value on the class under test
classToTest = new ClassToTest();
template = mock(Template.class);
classToTest.setTemplate(template);
when(template.getTemplates()).thenReturn(templates);
List<TemplateDto> result = classToTest.getTemplates("template1");
I have a testclass with #Parameters that looks like this:
#Parameterized.Parameters
public static Collection<Object[]> data() {
log.info("Starting test class for laan documents");
controller = new Controller();
List<Laan> calculations = controller
.getXMLValues(Controller.Type.LAAN)
.stream()
.map(object -> (Laan)object)
.collect(Collectors.toList());
ArrayList<Path> xmlFiles = new ArrayList<>();
calculations.forEach(s -> xmlFiles.add(s.getXmlPath()));
log.info("Getting log for " + xmlFiles.size() + " laan files");
logvalues = controller.getLogvalues(xmlFiles);
Collection<Object[]> params = new ArrayList<>();
for(Laan laan : calculations) {
if(laan.getDocumentType().equalsIgnoreCase("laan")) {
for(Logfile logfile : logvalues) {
if(logfile.getName().equalsIgnoreCase(laan.getName())) {
params.add(new Object[]{laan.getName(), laan, logfile});
}
}
}
}
log.info("");
return params;
}
Most of the objects contains the same variables with different values, but there are some cases where there is nothing to compare with in the logfile (which is no problem and in most cases normal). Those cases are handled with a test in the beginning of the test method assumeTrue(someCondition);
Now the test class is getting very big and hard to maintain. At the moment there is about 30 test methods in the class and there will be added about 100 new test methods in the upcoming days. I've read that it's bad practise to create a new class and use pointers to other test classes.. So how should I go ahead to and organize my very long testclass?
You need to define your architecture of your test framework. Try to seperate methods by another directories and classes. For example:
If you need to test some functionality like: LoginPage, Dashboard, Email - it doesn't mean that you need to keep all methods in one test class. Create basic api like:
com.myproject.steps
LoginPageSteps.java
DashBoardSteps.java
com.myproject.suite
TestSuite.java
com.myproject.scenarious
LoginPageScenario.java
DashboardScenario.java
After, if you have methods for working only with Dashboard, move them into DashBoardSteps, and for login methods into LoginPageSteps. After, call them from LoginPageScenario or DashboardScenario, this approach will provide you more comfortable conditions for easy support on future.
Im testing methods of SubList(Java.util.List) native implementation. Im using Java 8 api documentation. For toArray() method, I have only 3 tests. Can u help me to come up with more test cases? Thanks
#Test
public void toArrayBasicSub() {
fillList();
List<String> expectedList = Arrays.asList("1","2","3","4","5");
String[] expected = {"1","2","3","4","5"};
List<String> sub = list.subList(0, 5);
Object[] actual = sub.toArray();
assertEquals(expectedList,list);
assertArrayEquals(expected,actual);
}
#Test
public void toArrayEmpty() {
String[] expected = {};
List<String> sub = list.subList(0, 0);
Object[] actual = sub.toArray();
assertArrayEquals(expected,actual);
}
#Test
public void toArrayNull() {
fillList();
List<String> expected = null;
List<String> sub = list.subList(0, 5);
sub = null;
boolean thrownException = false;
try {
sub.toArray();
} catch (NullPointerException e) {
thrownException = true;
}
assertTrue(thrownException);
}
I assume you are targeting List.subList() for learning purposes only (as there are no other good reasons to test JDK methods!).
So, you are basically looking for feedback ... so lets help with that.
First thing: be clear about the scope you are testing. You say you want to test subList(). If so, then your tests should only be about sublist.
And then, the number of reasonable tests is really short:
Test the result for calling subList on an empty list
Test the result for calling subList on an non-empty list (simple case: with one element to show up in subList)
Test the result for calling subList on an non-empty list (complex case: with several elements to show up in subList)
You don't need much else. Specifically: when your job is to test the subList() call; then you don't need any test case for toArray.
subList() is called on a List, and returns a List. That is the only thing that matters here. What one does afterwards, to a list created by a call to subList() ... is not in the scope of testing subList()!
I wrote the dynamoDB code which stores list of items.
mapper.batchSave(trafficSensorReadings)
This will return.
List<FailedBatch>
I want to mock the mapper.batchSave and then return one failed job. How can I achieve it? I am using mockito and Junit.
I wrote something like this. But not useful.
when(dynamoDBMapper.batchSave(eq(List.class))).thenReturn(mock(List.class));
A complete example follows
#Test
public void test() {
FailedBatch failedBatch = mock(FailedBatch.class);
List<FailedBatch> failedBatchList = new ArrayList<>();
failedBatchList.add(failedBatch);
DynamoDBMapper dynamoDBMapperMock = mock(DynamoDBMapper.class);
when(dynamoDBMapperMock.batchSave(any(List.class))).thenReturn(failedBatchList);
tested.testedMethodCall();
verify(dynamoDBMapperMock).batchSave(any(List.class));
}
First, I think you might want to use Mockito.any() instead of Mockito.eq().
Second, I don't see why you would want to mock the list. You can just create one and return it
// GIVEN
FailedBatch batch1 = /**/;
FailedBatch batch2 = /**/;
List<FailedBatch> failedBatchList = Lists.newArrayList(batch1, batch2);
// WHEN
when(dynamoDBMapper.batchSave(any(List.class))).thenReturn(failedBatchList);
Object someResult = yourFunctionTestCall();
// THEN
verify(someResult)...
I am just getting started with unit testing. I did the junit tutorial from a pdf from the tutorial points website. So my question is, I want to test my shunting yard algorithm and my RPNEvaluator.
The constructors (and any other variables to help you out with the context) look like this:
ShuntingYard.java:
private ArrayList<String> tokens = new ArrayList<String>();
public ShuntingYard(ArrayList<String> tokens) {
this.tokens = tokens;
}
RPNEvaluator.java:
private Queue<String> polishExpression;
public RPNEvaluator(Queue<String> exp) {
polishExpression = exp;
}
ShuntingYard.java has a method called toRpn() which will take an ArrayList and return a Queue after some processing.
RPNEvaluator has a method called evaluate which will take a Queue type and return a double after some processing.
With Junit I am trying to write some unit tests and I wanted to know if this start was the best way to go about it:
package testSuite;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
public class ExpressionEvaluationTest {
/**
* Initialise the lists to be used
*/
#Before
public void beforeTest() {
ArrayList<String> exprOne = new ArrayList<String>();
exprOne.add("3");
exprOne.add("+");
exprOne.add("4");
exprOne.add("*");
exprOne.add("2");
exprOne.add("/");
exprOne.add("(");
exprOne.add("1");
exprOne.add("-");
exprOne.add("5");
exprOne.add(")");
exprOne.add("^");
exprOne.add("2");
exprOne.add("^");
exprOne.add("3");
ArrayList<String> exprTwo = new ArrayList<String>();
exprTwo.add("80");
exprTwo.add("+");
exprTwo.add("2");
ArrayList<String> exprThree = new ArrayList<String>();
exprThree.add("2");
exprThree.add("/");
exprThree.add("1");
exprThree.add("*");
exprThree.add("4");
ArrayList<String> exprFour = new ArrayList<String>();
exprFour.add("11");
exprFour.add("-");
exprFour.add("(");
exprFour.add("2");
exprFour.add("*");
exprFour.add("4");
exprFour.add(")");
ArrayList<String> exprFive = new ArrayList<String>();
exprFive.add("120");
exprFive.add("/");
exprFive.add("(");
exprFive.add("10");
exprFive.add("*");
exprFive.add("4");
exprFive.add(")");
ArrayList<String> exprSix = new ArrayList<String>();
exprSix.add("600");
exprSix.add("*");
exprSix.add("2");
exprSix.add("+");
exprSix.add("20");
exprSix.add("/");
exprSix.add("4");
exprSix.add("*");
exprSix.add("(");
exprSix.add("5");
exprSix.add("-");
exprSix.add("3");
exprSix.add(")");
}
#Test
public void test() {
}
}
I was going to put this in the before() method:
ShuntingYard sy = new ShuntingYard(/arraylist here/);
And then in the test, pass the lists to the algorithm. My question is that I think I am going the long way around it, would it be better to have a parameterised annotation and pass those lists as a list of parameters?
and a further question: if a test for any of the ArrayLists passes then I am sure I can execute a subsequent test to the RPNEvaluator evaluate method. I hope I haven't been ambiguous.
Help would be very much appreciated.
I would come at it a little differently. Instead of just creating several sets of test data and calling the same test each time break it up in to something meaningful. Instead of writing one test called test() write several separate tests for each aspect of ShuntingYard. For example:
#Test public void
itDoesntDivideByZero()
{
ArrayList<String> divideByZeroExpression = Arrays.asList("5", "0", "/");
// Add code to call your method with this data here
// Add code to verify your results here
}
#Test public void
itCanAdd()
{
ArrayList<String> simpleAdditionExpression = Arrays.asList("1", "2", "+");
// Add code to call your method with this data here
// Add code to verify your results here
}
and so on. This will make your JUnit output much easier to read. When there's a failure you know that it failed while trying to add, or it failed while trying to evaluate an expression that would cause a divide by zero, etc. Doing it the way you have it in the original you'd only know that it failed in the test() method.
Each of the tests here does 3 things:
Arranges the test data
Performs some action with that data
Asserts that the results of the action are as expected
This Arrange, Assert, Act idiom is very common in automated testing. You may also see it called Given, When, Then as in, "Given these conditions, when I call this method, then I should get this result".
Try to get out of the mindset of writing one test to test an entire class or method. Write a test to test one part of a method. Consider this class:
public class Adder {
public int addOneTo(int someNumber) {
return someNumber + 1;
}
}
You might end up with a test suite that looks like:
#Test public void
itAddsOne()
{
int numberToAddTo = 1;
int result = new Adder().addOneTo(numberToAddTo);
assertEquals("One plus one is two", 2, result);
}
#Test(expected="NullPointerException.class") public void
itChokesOnNulls()
{
new Adder().addOneTo((Integer)null);
}
#Test public void
itDoesntOverflow()
{
int result = new Adder().addOneTo(Integer.MAX_VALUE);
// do whatever here to make sure it worked correctly
}
And so on.
The advise from Mike B is very good, try to separate your test thinking in one test per behavior/functionality.
For make your test more readable i probably write a static constructor for the class ShuntingYard that receives a string, then you can write:
ShuntingYard addition = ShuntingYard.createFromExpresion("2+2");
assertThat(addition.getRpn().evaluate(), is(4));
you can refactor a little more and ends with something like that:
assertThat(evaluate("2+2"), is(4))
That is easy to understand an and easy to read, and in addition write more test with diferent scenarios its one-line of code.
Other option its to write parametrized test, one example: http://www.mkyong.com/unittest/junit-4-tutorial-6-parameterized-test/, but in my opinion are really ugly. This test are normally called "data driven test" and are used when you want to test the same code with different input values.
For this data-driven test a much better option its to use something like spock, a groovy framework for testing that allows you to write incredible semantic test, and of course you can use for testing java code, check this out: http://docs.spockframework.org/en/latest/data_driven_testing.html