I am creating some junit test cases using EasyMock. So far it makes sense for classes I am expecting to return POJOs, but how should I handle dealing with a DAO object that itself could throw an exception. My test case is to check for an expected exception thrown when the DAO encounters an issue. Using EasyMock I try and mock the DAO object (testing from the foo class), what is the correct way to handle the lower level DAO exception.
An example of the classes/simple calls is below: (Assume all getters/setters/constructors are valid and present)
public class foo{
private daoClass dao = daoClass.getInstance();
public String getValueFromDB(String key) throws DBException{
return dao.lookup(key);
}
}
public class daoClass{ //singleton DAO
public daoClass getInstance(){
//singleton access here
}
public String lookup(String key) throws DBException{
try{
//DB LOGIC
}
catch(Exception e){
throw new DBException(e.getMessage());
}
}
}
When I try and test the foo class, I want to be able to test for this DBException. How do I handle this, should I be surronding the DAO call in a try/catch (in the test), or add a throws to the test? I know expected=DBException will pass the test if that is thrown, but how syntactically should you handle any number of inner exceptions?
Test code Example:
#Test(expected=DBException.class)
public void testFooError(){
String key = "test";
String value = "expected";
daoClass daoMock = createMock(daoClass.class);
try{
expect(daoMock.lookup(key)).andReturn(value);
} catch (DBException e){
// ???
}
}
What is the correct way to handle when the expect could potentially throw errors? Should the test method throw the exception, or should a try/catch be used? Is it still correct to use the expected=EXCEPTION tag on the test?
Here's how I handle exceptions in unit tests:
If you aren't explicitly testing for the exception then you should add a throws clause to the method - if the exception was thrown and you weren't expecting it to be thrown then that's a test fail.
e.g.,
#Test
public void testFooNormal() throws DBException{
String key = "test";
String value = "expected";
daoClass daoMock = createMock(daoClass.class);
expect(daoMock.lookup(key)).andReturn(value);
// do an assert on returned value
...
}
If you are explicitly testing for the exception then put a try-catch around the line you expect it to be thrown from (catching the narrowest version of the Exception you expect) and then set a boolean in the catch clause and the assert should be on the value of the boolean.
e.g.,
#Test
public void testFooError(){
String key = "test";
String value = "expected";
boolean exceptionThrown = false;
daoClass daoMock = createMock(daoClass.class);
try{
expect(daoMock.lookup(key)).andReturn(value);
}catch (DBException e) {
exceptionThrown = true;
}
// assert exceptionThrown is true
...
}
This is a good way to test exceptions because it means you are testing not only that the correct exception is thrown but also that it's thrown from exactly the line you expect. If you use #test(expected=...) then a different line in the test could throw that exception and the test could incorrectly pass.
Your DAO logic should not change based on your test . If you are expecting your DAO to throw the exception then keep it that way and test the Exception in the way you are testing .
If you are catching the exception in DAO itself to do rollback or logging etc. , then the Test Case shouldn't expect Exception as it is not a test case scenario. You may check for NULL in this case as I expect your DAO will be returning NULL .
Related
I'm using Java 8 Functions and converters and have the following:
Main class
public final class MainClass {
public MainClass(
final Function<InputModel, OutputModel> businessLogic,
final Converter<Input, InputModel> inputConverter,
final Converter<OutputModel, Output> outputConverter) {
this.businessLogic = requireNonNull(businessLogic, "businessLogic is null.");
this.inputConverter = requireNonNull(inputConverter, "inputConverter is null.");
this.outputConverter = requireNonNull(outputConverter, "outputConverter is null.");
}
/**
* Request Handler.
*/
public Output handleRequest(final Input input) {
requireNonNull(input, "input is null.");
log.info("input request: {}", input);
try {
return inputConverter
.andThen(businessLogic)
.andThen(outputConverter)
.apply(input);
} catch (final Exception ex) {
throw new InternalServiceException(ex.getMessage(), ex);
}
}
}
Unit test
public final class TestClass {
#Mock
private Function<InputModel, OutputModel> mockedBusinessLogic;
#Mock
private Converter<Input, InputModel> mockedInputConverter;
#Mock
private Converter<OutputModel, Output> mockedOutputConverter;
private MainClass mainClass = new MainClass(mockedBusinessLogic, mockedInputConverter, mockedOutputConverter);
#Test
public void handleRequest_SHOULD_throwException_WHEN_inputConverter_throwsException() {
final RuntimeException inputConverterException = new NullPointerException(EXCEPTION_MESSAGE);
// what should I mock here? apply or convert.. apply for `Converter` seems to be deprecated.
when(mockedInputConverter.convert(input))
.thenThrow(inputConverterException);
final Exception exception = assertThrows(
InternalServiceException.class,
() -> mainClass.handleRequest(input)
);
assertThat(exception.getMessage(), is(EXCEPTION_MESSAGE));
assertThat(exception.getCause(), is(inputConverterException));
}
}
The above assertions fail.
I expect that if the inputConverter throws an exception, the catch block in handleRequest would wrap it to InternalServiceException, but it doesn't seem to be happening.
Any help?
How do I actually write unit tests for handleRequest method? I want to test the behavior when either of inputConveter, businessLogic or outputConveter throws exception.
Everything in your code is a mock. When you call andThen on your mocked inputConverter, then either null or a new mock instance is returned (depending on configuration). Each andThen will return a new instance with the chained converters (at least that is what I assume)
Make sure you mock all required methods, or better, use real objects instantiated from real classes.
Setting breakpoints and then debugging should help you find the issue. If you set in your try-block, and then single-step through the code, you will see that the way mocks are used in your code will not work. You could also save each result of andThen in a variable and then check in the debugger what type each has. I'm pretty sure it will either be null or "Mock for class X".
I'm making unit tests for a method that does some exception handling. here's the simplified class I'd like to test:
class Foo{
private BarService bar;
public int MethodToTest(){
try{
bar.methodThatThrows();
return 1;
}catch(Exception e){
return 0;
}
}
}
And this is the unit test class.
class FooTest{
private IBarService barService = mock(BarService.class);
#Test
TestMethodToTest(){
when(barService.methodThatThrows()).thenThrow(new Exception("message");
Foo foo = new foo();
ReflectionTestUtils.setField(foo, "barService", barService);
assertEquals(foo.MethodToTest(), 0);
}
}
Somehow, when I run it, it fails because there is an error thrown (as expected), which has the exact same message as the message I put into the mocked service. When I run in debug mode, the catch block is not even run. How can this be possible?
Most likely you are throwing a checked exception in your test that is not declared for methodThatThrows
A message you declared in your test is indeed printed to the console, but the message is more informative:
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.lang.Exception: message
For example (IOException declared in BarService, but a more general checked exception thrown in the test code):
public class BarService {
public int methodThatThrows() throws IOException {
return 1;
}
}
You're not setting the BarService correctly in your example code. You're doing:
ReflectionTestUtils.setField(foo, "barService", barService);
But in the Foo class, the BarService variable is called "bar", and not "barService", so you need to do:
ReflectionTestUtils.setField(foo, "bar", barService);
Though the "correct" way to do this is to use Spring to autowire BarService into Foo, which allows you to avoid having to use ReflectionTestUtils in the first place.
My application uses Google Guice dependency injection framework. Im now having trouble figuring out a way to write unit tests for my class.
private final Person aRecord;
#Inject
public MyClass(Venue venue, #Assisted("record") Record myRecord) {
super(venue,myRecord);
aRecord = (Person) myRecord;
}
public void build() throws Exception {
super.build();
super.getParentRecord().setJobType(aRecord.getJobType());
super.getParentRecord().setHairColor(aRecord.getHairColor());
super.getParentRecord().setEyeColor(aRecord.getEyeColor());
}
I want to write a unit test for the build() method in the child class, it should
Ensure that when super.build() gets called all of the basic information on the super.getParentRecord() is populated e.g. age, gender etc.
Ensure an exception is thrown in the build() method if aRecord.getJobType() is null
Ensure an exception is thrown in the build() method if aRecord.getHairColor() is null
Ensure an exception is thrown in the build() method if aRecord.getEyeColor() is null
You have two dependencies of MyClass (Venue and Record) so you have to mock those.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
...
Venue venueMock = mock(Venue.class);
Record recordMock = mock(Record.class);
Then in your unit test you have to create an instance of MyClass and assert the expected result:
For example: "Ensure an exception is thrown in the build() method if aRecord.getJobType() is null"
#Test(expected=RuntimeException.class)// or whatever exception you expect
public void testIfExceptionIsThrownWhengetJobTypeReturnsNull() throws Throwable {
Venue venueMock = mock(Venue.class); //create the mocks
Record recordMock = mock(Record.class);//create the mocks
when(recordMock.getJobType()).thenReturn(null); //specify the behavior of the components that are not relevant to the tests
MyClass myClass = new MyClass(venueMock, recordMock);
myClass.build();
//you can make some assertions here if you expect some result instead of exception
}
Note that if you don't specify the return value of any of the mocked dependencies (with when()) it will return the default value for the return type - null for objects, 0 for primitive numbers, false for boolean, etc. So it will be best to stub all of the methods that are used in MyClass.
I'm using the annotation #Mock(answer=Answers.RETURNS_SMART_NULL) with Mockito 1.9.5 in order to get some SmartNullPointerException when some unexpected mock calls occurs.
Unfortunately, the test pass, even without mocking at least one important call.
To be clear : My point is not to find by myself what I'm missing but to fail the test because I didn't mock the methods. I would want to do it without using Mockito.verifyNoMoreInteractions(...)
My test :
#RunWith(MockitoJUnitRunner.class)
public class ContextServiceImplTest {
#Mock(answer = Answers.RETURNS_SMART_NULLS)
private IAccountDAO accountDaoMock;
#Mock(answer = Answers.RETURNS_SMART_NULLS)
private IRuleService ruleServiceMock;
#Mock(answer = Answers.RETURNS_SMART_NULLS)
private ISensorDAO sensorDAOMock;
private ContextServiceImpl contextService;
#Before
public void before() {
contextService = new ContextServiceImpl(accountDaoMock, ruleServiceMock, sensorDAOMock);
}
#Test
public void fillSensor() throws Exception {
// given
String givenSensorId = "123"
final EventDTO givenEvent = new EventDTO();
givenEvent.setSensorId(givenSensorId)
// when
final Context context = contextService.getContext(givenEvent);
// then (should fail and throw explicit exception
}
The code to be tested :
public class ContextServiceImpl {
...
public Context getContext(final EventDTO event) throws Exception {
final String sMethodName = "getContext";
final String sensorId = event.getSensorId();
Context context = new Context();
try {
final Sensor sensor = sensorDAO.findById(sensorId);
context.setSensor(sensor);
return context;
} catch (final NoResultException nre) {
throw new BusinessException(ValidationCode.UNSUPPORTED_VALUE, "sensorId");
} catch (final PersistenceException pe) {
throw new TechnicalException(TechnicalCode.DATABASE_ACCESS_PROBLEM);
}
}
Thanks for your comments / advices / explainations.
You need to call a method on the null object sensor for the test to fail.
It seems like you are never using the null object sensor.
This means you won't get any NullPointerExceptions (smart or not)
you could do an
AssertEquals(context.getSensor().someMethod(), expectedResult);
or just a
context.getSensor().someMethod();
to get an exception.
But
AssertNotNull(context.getSensor());
won't be sufficient to get the SmartNullPointerException
Your tests will still pass because you're not invoking anything with the result of any of those mocked calls.
RETURNS_SMART_NULLS only throws a SmartNullPointerException if you attempt to dereference something that is a SmartNull. In essence, it's telling you that you're dereferencing a null value in a friendlier way, and you're not doing that with your current code.
First - you new up a Context. Unless you're getting into the weeds of threading problems, that will never come back null.
Next - you're passing in a newed EventDTO. For the same reasons above, that won't be null either.
The result of sensor may be null, but it doesn't matter - you're merely passing it through to the context without invoking any logic on it.
I'm not convinced that RETURNS_SMART_NULLS is going to work the best for your scenario. I'd recommend that you remove those answers from the mock objects. This will cause your test to fail because you haven't expected any of those interactions.
Here is the code that I am working with. In this test I want to verify that the log method is being called when an exception is caught.
public class SuperClass(){
public void log()
{
do some logging;
}
}
public class ClassUnderTest extends SuperClass(){
public String methodbeingtested(Object param)
{
try
{
String a = SomeObject.
methodthatthrowsexception(param);//static method, throws JAXB/NPE
}
catch(Exception exp)
{
log("log msg",exp);//inherited method
}
}
}
public class ClassUnderTestTest {
#Test
public testmethodbeingtested(){
ClassUnderTest cut = new ClassUnderTest()
ClassUnderTest cutspy = Mockito.spy(cut);
cutspy.methodbeingtested(param);
Mockito.verify(cutspy).log("log msg", new Exception()); // exp is needed to here.
}
}
After looking at several samples, the above was the closest I could get. This testcase forces an exception. But it fails to verify the log method call as Mockito.verify requires the exact exception (exp) that is caught, which the test case does not have access to.
Is there any other way to test this scenario?
Mockito's verify method can be used with argument matchers. If you want to verify that log was called, with any Exception at all as the second argument, you can just write
verify(cutspy).log(eq("log msg"), any(Exception.class));
I've assumed that you have the right static imports for verify, eq and any.
As an aside, this test does not need PowerMock. Your line PowerMock.expectLastCall().once(); is both redundant and confusing, and should probably be removed, along with the #PrepareForTest annotation.
Instead of spying on ClassUnderTest, you should mock the logging framework, inject it into the class and then verify that the log method gets called. Id' also mock the SomeObject class and have it throw exception.
As an aside, you should really evaluate if you need to verify your log statements. Perhaps you have a valid reason to do so but typically, asserting/verifying to this extent is not required and will only make your tests brittle.