JUnit test cases for custom method - java

I'm studying for my first job interviews as Junior Java Developer and right now I'm trying to learn JUnit test cases. This is an example that I encountered and I must say it's really tricky for me (it's abstract code so I have no idea how to test it).
public class JuiceMaker {
public Juice makeJuice(final List<Fruit> fruits) throws RottenFruitException {
for (final Fruit fruit : fruits) {
if (FruitInspector.isFruitRotten(fruit)) {
throw new RottenFruitException(fruit.getName() + “ is rotten. Cannot make juice.”);
}
}
return Juicer.juice(fruits);
}
}
The only example I managed to create myself is this one:
JuiceMaker jm = new JuiceMaker();
#Test
public void isThrowingException() {
//when
try {
jm.throwsRuntime();
Assert.fail("Expected exception to be thrown");
} catch (RottenFruitException e) {
//then
assertThat(e)
.isInstanceOf(RottenFruitException.class)
.hasMessage((fruit.getName() + " is rotten. Cannot make juice.");
}
}
Any tips of what kind of tests I can perform on this piece of code? Thanks a lot for your help!

Welcome to JUnit, and good luck with your interviews!
First question to ask is what is the contract offered by this class? It takes a list of fruit, tests if any of the fruit are rotten, if so it throws an exception, otherwise it juices them. You can assume the "juice" method is tested elsewhere.
To me, that suggests these tests:
List with single good fruit
List with single rotten fruit
List with several good and one rotten fruit
Empty list
You could also test for null and invalid values, but that might be overdoing things just now.
Once you've decided what to test, then you can start thinking about implementing them. Looks like your implementation has a couple of errors, but you're heading in a good direction. You might find JUnit's "expected" parameter useful for testing for exceptions.

You seem to be instructing the JuiceMaker instance in your test to throw the exception to verify you can catch it.
You have to answer yourself whether that alone will exercise the loop iterating through the list of Fruit and the if() statement.
You can influence the JuiceMaker.makeJuice() better by passing different lists (null, empty, with no rotten fruit, with rotten fruit).
This way you are not forcing any exceptions but causing them - which exercises more paths through your code under test.
If you exercise the above scenarios, you should have a very decent test coverage of your method.
Hope this helps!

The two testcases you put up in your example-answer are going in the right direction, but only half way. Because both tests do not at all test your "class under test".
Reasonable tests would more look like:
public class JuiceMakerTest {
private JuiceMaker underTest;
#Before
public void setup() { underTest = new JuiceMaker; }
#Test(expected=RottenFruitException.class)
public void testThrowsOnRottenFruit() {
underTest.makeJuice(Collections.singletonList("rotten apple"));
}
#Test(expected=???)
public void testWithNullList() {
underTest.makeJuice(null);
}
#Test(expected=???)
public void testWithEmptyList() {
underTest.makeJuice(Collections.emptyList());
}
#Test
public void testXyz() {
Juice expectedResult = ...
assertThat(underTest.makeJuice(Collections.singletonList("apple")), is(expectedResult);
}
and so on. In other words: you follow the nice answer from hugh; and determine the possible ways to invoke your method. The ??? is just a place holder - indicating that you should think up what should happen here. Maybe you expect a specific exception; maybe the method returns a special empty juice ... all up to the contract of the method under test.
You derive error conditions from that, and expected results. And then you write at least one test for of the different aspects you collected.

Related

How can I conduct a JUnit test which relies on the operation of another method?

I wish to conduct a JUnit-4 test for a method that reverts the effect of another method such that the test won't just pass if the first method is broken but instead requires both to work correctly. How can I address this?
Consider my following code...
#Test
public void testRemoveCategory(){
Category newCategory = new Category();
testRecipe.addCategory(newCategory);
testRecipe.removeCategory(newCategory);
assertFalse(testRecipe.getCategories().contains(newCategory));
In this code, the method removeCategory(Category category) can logically only remove a Category if the method addCategory(Category category)\ has already added it. This creates two problems
addCategory must work for removeCategory to pass the test
If addCategory doesn't work, removeCategory WILL pass the test even if it doesn't work either.
I cannot think of a solution to the first problem, but I solved the second problem by using the following code...
#Test
public void testRemoveCategory(){
Category newCategory = new Category();
boolean passesTest;
testRecipe.addCategory(newCategory);
if (!testRecipe.getCategories().contains(newCategory)){
passesTest = false;
}
else
{
testRecipe.removeCategory(newCategory);
if(!testRecipe.getCategories().contains(newCategory))
{
passesTest = true;
}
else
{
passesTest = false;
}
}
assertTrue(passesTest);
}
This however,
is clearly a bad workaround.
still does not solve the first problem.
I can see this general situation occurring often as many methods do serve as inverses of each other. How can I address these issues?
Unit tests are focused on only one unit's behaviour.
testRemoveCategory should not bother if addCategory is working correctly. You may add new categories by reflection, by other methods such as addCategories. You can not test all permutations of the methods.
I do not know the implementation details of how Recipe stores the categories. If the category holder is not passed by constructor or injected, you may try the approach below:
Define the initial state of unit under the test at given block
Try to change the state of unit at when block
And then, Assert the final state
#Test
public void testRemoveCategory() {
// given
Category otherCategory = new Category();
Category newCategory = new Category();
testRecipe.addCategory(otherCategory);
testRecipe.addCategory(newCategory);
// at this point testRecipe should have only two categories: newCategory and otherCategory
// you may add the categories by any other way not only addCategory()
// we are not testing the behaviour of addCategory
// when
testRecipe.removeCategory(newCategory);
// then
// testing the absence of newCategory
assertFalse(testRecipe.getCategories().contains(newCategory));
// testing the presence of otherCategory
assertTrue(testRecipe.getCategories().contains(otherCategory));
// !! but not testing only size of categories
// which may be flaky if there is a bug in removeCategory()
// assertTrue(testRecipe.getCategories().size(), 1);
}
Finally, If and else's are red flags in unit tests.
One should never have to use it.
Hope, it helps.

If you want to use assertThrows while testing, should you do that with stubs or mocks?

I have this Method that throws an IllegalArgumentException when somebody tries to call it with value 0.
I want to write several stub and mock tests - for example - for the method getFrequentRenterPoints.
I coudn't figure out any "when" or "verify" statements which are used in mocks so I mixed parts of mocks and parts of stubs together and came up with this:
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
//given
Movie movieMock = mock(Movie.class);
//when
movieMock.getFrequentRenterPoints(0);
//then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}
Is it okay to have in a class with other Mocks, or if I want to use assertThrows should I change this into a stub? Or can I use assertThrows with mocks?
The answer from Benjamin Eckardt is correct.
But I try to approach this question from another point of view: when to use mocking? This is one of my favourite answers to that question.
So in practise:
Say your code is like (just guessing all the business objects & names...):
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// this is just the rest of code in which your test does not enter because
// of thrown exception
return somethingToReturn();
}
For this you do not need and you should not want to mock anything here.
But when things get more complicated like your method would be like:
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// What is this?
// It is injected in the Movie - say - like
//
// #Resource
// private RenterPointService renterPointService;
List<RenterPoints> unfiltered = renterPointService.getRenterPoints(renterId);
return filterToFrequent(unfiltered);
}
Now if you test renterId >= 1 what about this renterPointService how do you instantiate it to not get NPE? Say if it is injected and requires to pull up heavy framework for testing or it requires very heavy construction or so? You do not, you mock it.
You are testing the class Movie not the class RenterPointService so you should not bother to think how RenterPointService works but what it returns when used in the class Movie. Still: you do not mock the class Movie which you are testing.
Assuming using you are using Mockito and using annotations the mocking would be then done in your test class like:
#Mock
private RenterPointService renterPointService;
#InjectMocks
private Movie movie;
Then you would do mocking of methods for renterPointService like:
when(renterPointService.getRenterPoints(anyInt))
.thenReturn(someListContaineingMockRenterPointsForThisTest);
Usually you expect the tested production method to throw and not the mock or stub. I drafted it by using new Movie().
Furthermore in that case it does not really make sense to separate the calls into when and then because if movieMock.getFrequentRenterPoints(0); throws, assertThrows(...) will never be executed.
To apply the given/when/then structure with the assertThrows API you could extract the passed lambda in some way, but I personally don't see much benefit in it.
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
// given
Movie movieMock = new Movie();
// when/then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}

Should all of my unit tests always pass when method is given bad input?

I am somewhat new to test driven development and am trying to determine whether I have a problem with my approach to unit testing.
I have several unit tests (let's call them group A) that test whether my method's return is as expected.
I also have a unit test "B" whose passing condition is that an IllegalArgumentException is thrown when my method is given invalid input.
The unit tests in group A fail when the method is given invalid input since the method needs valid input to return correctly.
If I catch the exception, unit test "B" will fail, but if I don't catch the exception, the tests in group A will fail.
Is it OK to have unit tests fail in this way, or can I modify the code in some way so that all tests always pass?
Am I doing TDD all wrong?
Here's a notion of my code for more clarity:
public class Example{
public static String method(String inputString, int value){
if(badInput){
throw new IllegalArgumentException();
}
//do some things to inputString
return modifiedInputString;
}
}
public class ExampleTests{
#Test
public void methodReturnsIllegalArgumentExceptionForBadInput(){
assertThrows(IllegalArgumentException.class, ()->{Example.method(badInput,badValue);})
}
//"Group A" tests only pass with valid input. Bad input causes IllegalArgumentException
#Test
public void methodReturnsExpectedType(){
assertTrue(actual == expected);
}
#Test
public void methodReturnsExpectedValue(){
assertTrue(actual == expected);
}
#Test
public void methodReturnsExpectedStringFormat(){
assertTrue(actual == expected);
}
}
As you've correctly noted in the comment the problem is with too broad test setup and too implicit tests.
Tests are much more readable when they are self-contained on the business level. Common test setup should be focused on setting up technical details, but all business setup should be within each test itself.
Example for your case (a conceptual example, must be reworked to match the details of your implementation):
#Test
public void givenWhatever_whenDoingSomething_methodReturnsExpectedType(){
given(someInputs);
Type result = executeSut(); //rename executeSut to actual function under test name
assertTrue(result == expected);
}
This way by just looking at a test a reader knows what scenario is being tested. Common test setup and helper functions such as given abstract away technical details, so the reader does not get distracted at the first inspection. If they are interested, the details are always available - but are usually less important, such can be hidden.

writing a junit test case for the calling of a method

i am using eclemma and trying to increase my test coverage:
so far this is my code:
public RolesResponse findRolesByTenant(RolesRequest rolesRequest)
{
RolesResponse rolesResponse = new RolesResponse();
List<Role> roleList = null;
if (StringUtils.isNotBlank(rolesRequest.getTenantCode()))
{
roleList = roleFunctionService.getAllRolesAndFunctionsByTenant(rolesRequest.getTenantCode());
}
if (CollectionUtils.isNotEmpty(roleList))
{
rolesResponse.setRoles(roleList);
}
else
{
rolesResponse.setError(LayerContextHolder.getErrorObject());
}
return rolesResponse;
}
and here is my test:
#Test
public void findRolesByTenantTest()
{
RolesRequest rolesRequest = new RolesRequest();
rolesRequest.setTenantCode("test");
ErrorObject errorObject = new ErrorObject();
RolesResponse rolesResponse = rolesProcessService.findRolesByTenant(rolesRequest);
Assert.assertNull(rolesResponse.getError());
}
the only line eclemma is highlighting in red is this one:
rolesResponse.setError(LayerContextHolder.getErrorObject());
can someone help me in constructing the final test needed to cover this line
thanks
I'm really not a fan of your test anyway - what are you trying to prove by the error being null? That the list came back with something? Also, are you certain that your service will return the result you want in your test every single time?
Don't think of tests in terms of coverage; this will lead to brittle tests and tests that give a false sense of security. What you want to do is write tests that cover each condition that the code could encounter, and the line coverage can follow from that.
From your code, I see two cases.
roleFunctionService#getAllRolesByFunctionAndTenant can return a non-empty list.
It's implied that the resultant rolesResponse#roles contains whatever was in the list provided by the method, and this should be verified.
It's also implied that there is no error set on the object, so it should be null.
roleFunctionService#getAllRolesByFunctionAndTenant can return an empty list
Either the resultant rolesResponse#roles are empty or null; it'd be better if it were empty.
It's implied that there is an error on the object, which is specifically provided by LayerContextHolder.getErrorObject(). You should check to see that it's exactly that.
You'll get to the whole approach of writing this test through the use of a mocking framework.

Test method that returns void

I have a void method and I want to test it. How do I do that?
Here's the method:
public void updateCustomerTagCount() {
List<String> fileList = ImportTagJob.fetchData();
try {
for (String tag : fileList) {
Long tagNo = Long.parseLong(tag);
Customer customer = DatabaseInterface.getCustomer(tagNo);
customer.incrementNoOfTimesRecycled();
DatabaseInterface.UpdateCustomer(customer);
}
} catch(IllegalArgumentException ex) {
ex.printStackTrace();
}
}
when the method returns void, you can't test the method output. Instead, you must test what are the expected consequences of that method. For example:
public class Echo {
String x;
public static void main(String[] args){
testVoidMethod();
}
private static void testVoidMethod() {
Echo e = new Echo();
//x == null
e.voidMethod("xyz");
System.out.println("xyz".equals(e.x)); //true expected
}
private void voidMethod(String s) {
x = s;
}
}
It might not be always true, but basic concept of unit test is to check if function works as expected and properly handling errors when unexpected parameters/situation is given.
So basically unit test is against the functions that takes input parameters and return some output so we can write those unit test.
The code like yours, however, includes some other dependency (database call) and that's something you can't execute unless you write integration-test code or real database connection related one and actually that's not recommended for unit test.
So what you need to do might be introducing unit test framework, especially Mockto/Powermock or some other stuff that provides object mocking feature. With those test framework, you can simulate database operation or other function call that is going to be happening outside of your test unit code.
Also, about how do I test void function, there is nothing you can with Assert feature to compare output since it returns nothing as you mentioned.
But still, there is a way for unit test.
Just call updateCustomerTagCount() to make sure function works. Even with just calling the function, those unit test can raise your unit test coverage.
Of course for your case, you need to mock
ImportTagJob.fetchData();
and
DatabaseInterface.getCustomer(tagNo);
and have to.
Let mocked
ImportTagJob.fetchData();
throw empty list as well as non-empty list and check if your code works as you expected. Add exception handling if necessary. In your code, there are two condition depends on whether fieList are null or non-null, you need to test it.
Also, mock those objects and let them throw IllegalArgumentException where you expect it to be thrown, and write an unit test if the function throws a exception. In Junit, it should be like
#Test(expected = IllegalArgumentException.class)
public void updateCustomerTagCountTest(){
// mock the objects
xxxxx.updateCustomerTagCount();
}
That way, you can ensure that function will throw exception properly when it has to.

Categories

Resources