JUnit test with expected exception does not behave as anticipated - java

I came accross a problem with JUnit 4.12 I cannot explain when testing a method in my code which should throw a specific exception.
I'm using the #Test annotation with (expected = MyException.class).
MyException is simply derived from Exception.
Due to an programming error (missing 'throw' clause) this exception is not thrown. Because of the test case setup including mocked classes using jmockit 1.29, later in the method a NullPointerException is thrown.
Example classes shwoing the basic class layout:
package l00tr.test;
public class MyException extends Exception {
private static final long serialVersionUID = 1L;
public MyException(String msg) {
super(msg);
}
}
package l00tr.test;
import java.util.Map;
import java.util.Set;
public class MyClass {
private Set<String> testSet;
private Map<String, String> testMap;
private boolean condition;
private MyClass() {
this.testSet = null;
this.testMap = null;
this.condition = false;
}
private MyClass(Set<String> testSet, Map<String, String> testMap,
boolean condition) {
this.testSet = testSet;
this.testMap = testMap;
this.condition = condition;
}
public void method() throws MyException {
if (testSet.size() < 2) {
throw new MyException("test set");
}
if (!condition) {
// FORGOTTEN: throw
new MyException("test");
}
System.out.print("set size: " + testSet.size());
System.out.print("some more stuff succeeding");
System.out.print("map size: " + testMap.size()); // NullPointerException
}
public static final class Builder {
private Set<String> testSet;
private Map<String, String> testMap;
private boolean condition;
private Builder() {
this.testSet = null;
this.testMap = null;
this.condition = false;
}
public static Builder newBuilder() {
return new Builder();
}
public Builder testSet(Set<String> testSet) {
this.testSet = testSet;
return this;
}
public Builder testMap(Map<String, String> testMap) {
this.testMap = testMap;
return this;
}
public Builder condition(boolean condition) {
this.condition = condition;
return this;
}
public MyClass build() {
return new MyClass(testSet, testMap, condition);
}
}
}
So I would expect the test to fail. But it did succeed. Removing the "(expected = ...)" part of the Test annotation results in the test failing with the NullPointerException as to expect.
package l00tr.test;
import java.util.Set;
import org.junit.Test;
import mockit.Expectations;
import mockit.Mocked;
public class MyClassTests {
#Mocked
private Set<String> testSet;
#Test(expected = MyException.class)
public void testMethod() throws MyException {
new Expectations() {
{
testSet.size();
result = 3;
}
};
MyClass mc = MyClass.Builder.newBuilder().condition(false)
.testSet(testSet).build();
mc.method();
}
}
Jacoco helped to spot this, otherwise I would have wrongly assumed everything to be fine. I also checked the open issues for junit 4.12 but did not find a matching issue.
Why does the test succeed? Any ideas?
Update:
I improved the example to show the relevant details of my code. Unfortunately, the code pasted above does behave as expected, i.e. showing the error:
java.lang.Exception: Unexpected exception, expected<l00tr.test.MyException> but was<java.lang.NullPointerException>
My specific development environment setup may play a role, so here are some details:
gradle 3.2.1 with active daemon
junit 4.12
jmockit 1.29
eclipse Neon.1a Release (4.6.1)
java version "1.8.0_112" 64 bit
Windows 10

After the helpful comments, I've found out that the problem could be narrowed down to the "'false positive' problem with annotation based exception checking with Junit4":
After checking again the full stacktrace and code including an abstract superclass, I've found a try/catch converting any exception to MyException after logging. So from tool and code perspective, everything works as to expect.

Related

public void method throws Exception if - JUNIT test: Exception should not be thrown

I have a certain public void method which throws an Exception if a condition is fulfilled.
In my case the method looks like this:
public void toBeTestedMethod(Testobject testObject) throws CertainException {
if (testObject.getStatus().getAllowsEdit()){
throw ...}
}
getStatus() is a method which returns a certain Status and getAllowsEdit() is a method which returns a boolean value and nullable = true. For the two methods there also exist set-methods.
Edit1: The test regarding this method when it fails is already running fine:
public void testToBeTestedMethod_FailureStatus() throws Exception {
try {
TestObject testObject = _testObjectMockDAO.getNewTestObject();
_testObjectMockDAO.setTestObject(testObject);
_testObjectBusinessImpl.toBeTestedMethod(testObject);
fail("Check failed");
} catch (CertainException ex) {
assertEquals(ErrorCode.WRONG_STATUS, ex.getErrorCode());
}
}
I would now like to test the method toBeTestedMethod. The target is that the method does not throw an exception but gets executed successfully.
That means I would like to write a JUNIT-test which tests the following:
public void testToBeTestedMethod_success throws Exception{
// Enter test code here
}
Edit2 (regarding the class Status):
public class Status {
...
private String _status;
public String getStatus() {
return _status;
}
}
In my opinion, I have to modify the condition in the if-statement in order to get the expected result, correct?
Note: I did not write the method and the other code. Nevertheless, my task is to test the code via JUNIT.
I tried some code, but everytime I get the error that the Excpetion was thrown.
Even if you cannot solve this problem, I would be glad to get some hints where I should look for the problem why my test does not do what I want the test to do.
Your question is very abstract and needs more data, I am posting an answer here based on what I have understood.
Here are the classes:
public class SampleTestService {
public boolean toBeTestedMethod(TestObject testObject) throws AccessViolationException {
if (testObject.getStatus().getAllowsEdit()) {
throw new AccessViolationException("Edit is allowed for this non confirmed user");
} else {
return true;
}
}
static class TestObject {
private SomeStatus someStatus;
public SomeStatus getStatus() {
return someStatus;
}
}
static class SomeStatus {
private boolean allowsEdit;
public boolean getAllowsEdit() {
return allowsEdit;
}
}
static class AccessViolationException extends RuntimeException {
public AccessViolationException(String message) {
super(message);
}
}
}
Since the method depends on another class and that class-dependent also on another class you need to mock them in the chain. Here is how I have done it:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
#ExtendWith(SpringExtension.class)
class SampleTestServiceTest {
private final SampleTestService.TestObject mockTestObject = mock(SampleTestService.TestObject.class);
private final SampleTestService.SomeStatus mockSomeStatus = mock(SampleTestService.SomeStatus.class);
private final SampleTestService service = new SampleTestService();
#Test
void testThatMethodDoesNotThrowsException() {
when(mockTestObject.getStatus()).thenReturn(mockSomeStatus);
when(mockSomeStatus.getAllowsEdit()).thenReturn(false);
boolean result = service.toBeTestedMethod(mockTestObject);
Assertions.assertTrue(result);
}
#Test
void testThatMethodThrowsException() {
when(mockTestObject.getStatus()).thenReturn(mockSomeStatus);
when(mockSomeStatus.getAllowsEdit()).thenReturn(true);
Assertions.assertThrows(SampleTestService.AccessViolationException.class, () -> {
service.toBeTestedMethod(mockTestObject);
});
}
}

Test the error code of a custom exception with JUnit 4

I would like to test the return code of an exception. Here is my production code:
class A {
try {
something...
}
catch (Exception e)
{
throw new MyExceptionClass(INTERNAL_ERROR_CODE, e);
}
}
And the corresponding exception:
class MyExceptionClass extends ... {
private errorCode;
public MyExceptionClass(int errorCode){
this.errorCode = errorCode;
}
public getErrorCode(){
return this.errorCode;
}
}
My unit test:
public class AUnitTests{
#Rule
public ExpectedException thrown= ExpectedException.none();
#Test (expected = MyExceptionClass.class,
public void whenRunningSomething_shouldThrowMyExceptionWithInternalErrorCode() throws Exception {
thrown.expect(MyExceptionClass.class);
??? expected return code INTERNAL_ERROR_CODE ???
something();
}
}
Simple:
#Test
public void whenSerialNumberIsEmpty_shouldThrowSerialNumberInvalid() throws Exception {
try{
whenRunningSomething_shouldThrowMyExceptionWithInternalErrorCode();
fail("should have thrown");
}
catch (MyExceptionClass e){
assertThat(e.getCode(), is(MyExceptionClass.INTERNAL_ERROR_CODE));
}
That is all you need here:
you don't want to expect that specific exception, as you want to check some properties of it
you know that you want to enter that specific catch block; thus you simply fail when the call doesn't throw
you don't need any other checking - when the method throws any other exception, JUnit will report that as error anyway
You can check for it using hamcres matchers as long as thrown.expect is overload to receive Matcher
thrown.expect(CombinableMatcher.both(
CoreMatchers.is(CoreMatchers.instanceOf(MyExceptionClass.class)))
.and(Matchers.hasProperty("errorCode", CoreMatchers.is(123))));
Note that you will need to add hamcrest matcher to your dependencies. Core matched that are included in JUnit is not enough.
Or if you don't want to use CombinableMatcher:
thrown.expect(CoreMatchers.instanceOf(MyExceptionClass.class));
thrown.expect(Matchers.hasProperty("errorCode", CoreMatchers.is(123));
Also, you don't need (expected = MyExceptionClass.class) declaration for #Test annotation
Expanding upon Sergii's answer, you can clean this up even more by writing a custom matcher.
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class CustomMatcher extends TypeSafeMatcher<CustomException> {
public static CustomMatcher hasCode(String item) {
return new CustomMatcher(item);
}
private String foundErrorCode;
private final String expectedErrorCode;
private CustomMatcher(String expectedErrorCode) {
this.expectedErrorCode = expectedErrorCode;
}
#Override
protected boolean matchesSafely(final CustomException exception) {
foundErrorCode = exception.getErrorCode();
return foundErrorCode.equalsIgnoreCase(expectedErrorCode);
}
#Override
public void describeTo(Description description) {
description.appendValue(foundErrorCode)
.appendText(" was not found instead of ")
.appendValue(expectedErrorCode);
}
}
The error code can then be checked like:
import org.junit.rules.ExpectedException;
public class MyObjTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Test
public void someMethodThatThrowsCustomException() {
thrown.expect(CustomException.class);
thrown.expect(CustomMatcher.hasCode("110501"));
MyObj obj = new MyObj();
obj.methodThatThrowsCustomException();
}
}
Reference: https://dzone.com/articles/testing-custom-exceptions

NullPointerException when mocking controller class

i wanted to write some tests for the method shown in the code below. I'm mocking dependencies with JMockit. For whatever reason i am getting a NullPointerException all the time and i really don't understand why that happens. I'm new to JMockit and in mocking dependencies in general. Stack Trace just shows NullPointerException thrown at line new NonStrictExpectations()
.
Method to test:
#Override
public boolean addSubject(User user, Schedule schedule, SchoolSubject subject) {
final boolean result = schedule.addSubject(subject.getHourTime(), subject);
scheduleDAO.update(schedule);
if (subject.getTeacher() != null && !subject.getTeacher().trim().isEmpty()) {
for (final TeacherEntry teacher : user.getTeachers()) {
if (subject.getTeacher().equals(teacher.getName())) {
teacher.getSubjects().add(subject.getName());
teacherDAO.update(teacher);
}
}
}
try {
userDAO.update(user);
} catch (final DuplicateUniqueFieldException e) {
throw new UnexpectedUniqueViolationException(e);
}
}
Test method:
//imports not copied
public class ScheduleManagerTest {
#Tested
ScheduleManager manager;
#Injectable
UserDAO userDAO;
#Injectable
ScheduleDAO scheduleDAO;
#Injectable
TeacherEntryDAO teacherDAO;
#Injectable
SchoolSubjectDAO schoolSubjectDAO;
#Mocked
Schedule schedule;
#Mocked
SchoolSubject subject;
#Mocked
User user;
#Test
public void testAddSubject() throws DuplicateUsernameException, DuplicateEmailException {
new NonStrictExpectations() {
{
schedule.addSubject(anyInt, (SchoolSubject) any);
result = true;
scheduleDAO.update((Schedule) any);
subject.getTeacher();
result = anyString;
subject.getTeacher().trim().isEmpty();
result = false;
user.getTeachers();
result = (ArrayList<TeacherEntry>) any;
teacherDAO.update(((TeacherEntry) any));
userDAO.update((User) any);
};
};
assertTrue(manager.addSubject(user, schedule, subject));
}
}
I think i'm doing something pretty wrong :(
It's hard to know what is going wrong based on the description, but here's a guess:
Mocked objects will return null in their methods by default. You'll get a null pointer here:
subject.getTeacher().trim()
because getTeacher() will return null. To return a teacher, you'll have to either use a real subject or do further mocking with
Mockito.when(subject.getTeacher()).thenReturn(new Teacher());
You need to add #RunWith(JMockit.class) to your class
#RunWith(JMockit.class)
public class ScheduleManagerTest {
...

Mockito & Junit null pointer exception: name must not be null

I have been trying to run the following test using mockito and junit and I keep on getting "java.lang.NullPointerException: name must not be null"
Can anyone tell me why this is happening?
On debugging, I found out that this exception is thrown when the test executes the following statement in isStopValid(String) method:
FacilityValidationUtil facUtil = new FacilityValidationUtil();
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest{
#InjectMocks MyForm form = new MyForm();
#Mock FacilityValidationUtil facUtil;
#Test
public void testIsStopValid() throws FinderException{
when(facUtil.isFacilityValid("")).thenReturn(false);
form.setOrigin("");
assertEquals(false, form.isStopValid(form.getOrigin()));
}
}
Class with method to be tested:
public class MyForm{
FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop){
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
log.error("Error finding the stop. "+e.getCause());
return false;
}
}
}
public class FacilityValidationUtil{
private FacilityDAO facilityDao = new HibernateFacilityDAO();
public boolean isFacilityValid(String facility) throws FinderException{
boolean test;
FacilityImpl facilityImpl = facilityDao.findFacilityByNassCode(facility);
test = (facilityImpl==null)?false : true;
return test;
}
}
public class HibernateFacilityDAO extends HibernateAbstractDeltaDAO implements FacilityDAO {
public HibernateFacilityDAO() {
super(false);
}
}
Short Answer: You are trying to mock a variable (facUtil) that is local to your isStopValid method, so the mock version of this object in your test is never going to be called because you are 'newing it up" each time.
Long Answer: It looks like you are trying to mock the call to your FacilityValidationUtil class, and if this is the case, then you need to either make the class a field so that Mockito can inject the object by reflection (if this object is thread safe, which it looks like it is) or explore a mocking framework like PowerMockito that will allow you to mock a constructor (google for PowerMockito when new).
PowerMockito.whenNew(FacilityValidationUtil.class).withNoArguments().thenReturn(facUtil);
Mockito doesn't support any mocking of constructor args by default.
EDIT
If you are still having trouble, then I would suggest starting with a smaller example. I've put together one for you that works and uses the code you are trying to test (It's using inner classes though, which Mockito has some quirky rules about, but I'm just doing it to compress the example).
#RunWith(MockitoJUnitRunner.class)
public class MyFormTest {
#InjectMocks
private MyForm form = new MyForm();
#Mock
private FacilityValidationUtil facUtil;
#Test
public void testIsStopValid_false() {
when(facUtil.isFacilityValid("")).thenReturn(false);
assertEquals(false, form.isStopValid(""));
}
#Test
public void testIsStopValid_true() {
when(facUtil.isFacilityValid("")).thenReturn(true);
assertEquals(true, form.isStopValid(""));
}
public class MyForm {
private FacilityValidationUtil facUtil = new FacilityValidationUtil();
public boolean isStopValid(String stop) {
try {
return facUtil.isFacilityValid(stop);
} catch (FinderException e) {
return false;
}
}
}
public class FacilityValidationUtil {
public boolean isFacilityValid(String facility) throws FinderException {
throw new RuntimeException(facility);
}
}
public class FinderException extends RuntimeException {
public FinderException(String message) {
super(message);
}
}
}
What's really important is that your mock is not getting injected correctly. Until you get that resolved, you are going to keep getting the same error. Set a break point in your MyForm at the point you call facUtil.isFaciltyValid and look at the object. It should be a mockito object, not your class.
Good luck.

IllegalStateException with deep stubbing in Mockito

I'm getting a very strange error with Mockito:
java.lang.IllegalStateException
at java.util.LinkedList$ListItr.remove(LinkedList.java:923)
at org.mockito.internal.debugging.WarningsFinder.find(WarningsFinder.java:36)
at org.mockito.internal.debugging.WarningsPrinterImpl.print(WarningsPrinterImpl.java:28)
at org.mockito.internal.debugging.WarningsCollector.getWarnings(WarningsCollector.java:34)
at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:29) <26 internal calls>
There is no reference to my test code, but here are the classes I'm using and the test:
Endpoint.java: Uses Retrofit 2.0
public interface Endpoint {
#GET("items/{itemId}")
Call<List<Item>> list(#Path("itemId") String itemId);
Call<List<Item>> list(#Path("itemId") String itemId, #Path("alternativeId") String alternativeId);
}
Query.java
public class Query {
private String itemId;
// constructor, etc
public String getItemId() {
return itemId;
}
}
ItemDataSource.java: Unfinished implementation (following TDD)
public class ItemDataSource {
private Endpoint endpoint;
// constructor, etc
public Optional<Item> get(Query query) {
List<Item> list = null;
try {
list = endpoint.list(query.getItemId()).execute().body();
} catch (IOException e) {
e.printStackTrace();
}
Template result = modelAdapter.apply(list.get(0));
return Optional.ofNullable(result);
}
}
ItemDataSourceTest.java:
public class TemplateNetworkDataSourceTest {
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private Query query;
#Mock
private Item item;
#Mock(answer = RETURNS_DEEP_STUBS)
private ItemEndpoint endpoint;
#Test
public void shouldProvideItemFromEndpoint() throws Exception {
when(query.getItemId()).thenReturn("itemId");
when(endpoint.list("itemId").execute()).thenReturn(Response.success(singletonList(networkTemplate)));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.get()).isEqualTo(item);
}
#Test
public void shouldProvideNoItemsWhenNotFound() throws Exception {
//when(query.getProductId()).thenReturn("incorrect_productId"); // <- works
when(endpoint.list(anyString()).execute()).thenReturn(Response.success(emptyList()));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.isPresent()).isFalse();
}
}
If I run it, the first test runs correctly but the second gives me the provided error. If I comment out the first line in the statement of the second test it works (so that could be the answer) but I'm wondering if I'm missing something or if this is a bug on Mockito.
From my slight digging through Mockito's code I see that a List is modified when collecting the warnings after the test is executed.

Categories