Is it possible to test for multiple exceptions in a single JUnit unit test? I know for a single exception one can use, for example
#Test(expected=IllegalStateException.class)
Now, if I want to test for another exception (say, NullPointerException), can this be done in the same annotation, a different annotation or do I need to write another unit test completely?
You really want the test to do one thing, and to test for that. If you're not sure as to which exception is going to be thrown, that doesn't sound like a good test to me.
e.g. (in pseudo-code)
try {
badOperation();
/// looks like we succeeded. Not good! Fail the test
fail();
}
catch (ExpectedException e) {
// that's fine
}
catch (UnexpectedException e) {
// that's NOT fine. Fail the test
}
so if you want to test that your method throws 2 different exceptions (for 2 sets of inputs), then you'll need 2 tests.
This is not possible with the annotation.
With JUnit 4.7 you can use the new ExpectedException rule
public static class HasExpectedException {
#Interceptor
public ExpectedException thrown= new ExpectedException();
#Test
public void throwsNothing() {
}
#Test
public void throwsNullPointerException() {
thrown.expect(NullPointerException.class);
throw new NullPointerException();
}
#Test
public void throwsNullPointerExceptionWithMessage() {
thrown.expect(NullPointerException.class);
thrown.expectMessage("happened?");
throw new NullPointerException("What happened?");
}
}
More see
JUnit 4.7: Interceptors: expected exceptions
Rules in JUnit 4.7
If updating to JUnit 4.7 is not possible for you, you have to write a bare unit test of the form
public test() {
try {
methodCall(); // should throw Exception
fail();
}
catch (Exception ex) {
assert((ex instanceof A) || (ex instanceof B) || ...etc...);
...
}
}
Although this is not possible with JUnit 4, it is possible if you switch to TestNG, which allows you to write
#Test(expectedExceptions = {IllegalArgumentException.class, NullPointerException.class})
Use catch-exception:
// test
public void testDo() {
// obj.do(1) must throw either A or B
catchException(obj).do(1);
assert caughtException() instanceof A
|| caughtException() instanceof B;
// obj.do(2) must throw A but not SubclassOfA
catchException(obj).do(2);
assert caughtException() instanceof A
&& !(caughtException() instanceof SubclassOfA);
}
#Test(expected=Exception.class)
This will throw all possible exceptions.
How would you expect to "expected"s to work? A method can only throw one exception.
You would have to write a different unit test for each way the method can fail. So if the method legitimately throw two exceptions then you need two tests set up to force the method of throwing each exception.
Keep the tests as simple and short as possible. The intention of a JUnit-Test is to test only one simple functionality or one single way of failure.
Indeed, to be safe, you should create at least one test for every possible execution way.
Normally, this is not always possible because if you have a method that analyses a string, there are so many possible string combinations that you cannot cover everything.
Keep it short and simple.
You can have 30-40 testing methods for one single method easily... does it really matter?
Regards
Related
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);
});
}
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.
In TestNG, when we want to test a scenario in which an exception should be thrown one can write something like the following
#Test(expected=IndexOutOfBoundsException.class, expectedExceptionsMessageRegExp="*")
public void sampleExpectedExceptionTest() {
List emptyList = new ArrayList();
// Following line should throw an IndexOutOfBoundsException
emptyList.get(0);
}
I have seen that some people write the tests in following style.
#Test
public void sampleExpectedExceptionTest() {
// several lines here..
List emptyList = new ArrayList();
try {
emptyList.get(0);
Assert.assertFail("Expected IndexOutOfBoundsException but wasn't thrown");
} catch (IndexOutOfBoundsException e) {
// ignore
}
// ... more lines of code asserting exceptions similar to above try catch scenario
}
I dislike the above style primarily because it is very verbose and also because people using it usually write multiple tests in one test case. However, the argument given in it's favour is that it allows users to pinpoint the assertion to a particular line and hence it is better.
Recently I learnt about JUnit's #Rule annotation
public class SampleExceptionTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
#Test
public void sampleExpectedExceptionTest() {
List emptyList = new ArrayList();
exception.expect(IndexOutOfBoundsException.class);
emptyList.get(0);
}
}
This not only allows users to pinpoint the assertion to a line but it also discourages users to write multiple tests in one test case as once an exception is thrown the code exits and you can not test for multiple assertions. I want to know is there a similar option (or an idiom) in TestNG? I know that I can use expected
I suggest you have a look at the Catch-Exception library. It allows you to perform multiple assertions on the exception and any other relevant assertions. You can use it with JUnit and TestNG and any assertion framework you want.
Sample:
#Test
public void catchExceptionTest() {
// given: an empty list
List<Object> myList = new ArrayList<>();
// when: we try to get the first element of the list
// then: catch the exception if any is thrown
catchException(myList).get(1);
// then: we expect an IndexOutOfBoundsException
Exception e = caughtException();
// Use JUnit, TestNG, AssertJ etc. assertions on the "e" exception
assert e instanceof IndexOutOfBoundsException;
}
You'll find more examples on the project page.
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.
This is my code which I want to force to throw a Remote Exception:
transient Bicycle b=null;
public Bicycle getBicycle() {
if(b==null) {
try {
b=new Bicycle(this);
} catch (RemoteException ex) {
Logger.getLogger(Bicycle()).log(Level.SEVERE, null, ex);
}
}
return b;
}
Here is the JUnit test I am running with Mockito:
boolean exceptionThrown=false;
Bicycle mockB = mock(Bicycle);
mockB.setBicycle(null);
stub(mockB.getBicycle()).toThrow(new RemoteException(){boolean exceptionThrown = true;});
assertTrue(exceptionThrown);
I keep receiving the following error:
Checked exception is invalid for this method!
Any help will be appreciated.
Edit:
Instead of
stub(mockB.getBicycle()).toThrow(new RemoteException(){boolean exceptionThrown = true;});
I have also tried
doThrow(new RemoteException(){boolean exceptionThrown = true;}).when(mockB).getBicycle();
and
Mockito.when(mockB.getBicycle()).thenThrow(new RemoteException(){boolean exceptionThrown=true;});
Still no luck.
Edit2 - gone one step further after fully understanding the API and using it correctly:
when(mockB.getBicycle()).thenThrow(new RuntimeException());
I don't know how to make the assert now. I tried putting a boolean once the exception gets called but the assert cannot see the boolean.
Any ideas?
The getBicycle() method will never return a RuntimeException. The code itself is catching the RuntimeException and, when caught, writes to the logger. The method itself will either return the Bicycle or null.
You will need to rethink how you want the getBicycle method operates. It could re-throw the RuntimeException atfer logging if you want the RuntimeException to bubble through. But, based on how that's written, the RuntmeException will never make it out to the JUnit test