I'm trying to find the proper way to identify the dangerous usage of nullable library methods with SonarQube.
The example below illustrates the potential NullPointerException that may be raised by calling asText() method on the nullable JsonNode object returned from the node.get("something") call.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Tst {
public String getSomething(String json) throws Exception {
JsonNode node = new ObjectMapper().readTree(json);
return node.get("something").asText();
}
}
I expect to have the line return node.get("something").asText(); to be marked as the bug, but the problem is not identified by our SonarQube 9.8 Server.
How to let SonarQube understand that com.fasterxml.jackson.databind.JsonNode.get() method is nullable?
Related
I have currently this the issue that there is an object of Type CoolObj created within my method doSomething().
This object goes through some processes and at the end I want to verify if everything is ok and then continue.
Now the problem is that I cannot control the behavior and prevent an exception to be thrown.
Either hasErros() should return false or I kind of mute the verify method. But I could not figure out how.
Any idea how to solve this issue please?
public class ExampleClass {
public void doSomething(){
CoolObj coolObj = new CoolObj();
verify(coolObj);
}
private void verify(CoolObj coolObj) {
if(coolObj.hasErrors()){
throw new Exception(); //this is my issue
}
}
}
Basically, the problem that you have is related to the fact, that test doesn't control creation of CoolObj instances (that's because ExampleClass is not designed in testable way).
The best approach would be to change the design of ExampleClass, for example by delegating creating CoolObj instances to corresponding factory. Then mock instance created by factory and setup needed responses of hasErrors by Mockito.
However, if for some reason you are forced to leave the design of ExampleClass as is, then you can use PowerMock to mock construction of new instances.
For the code snippet provided in the question, following is an example of tests with and without exception thrown:
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExampleClass.class)
public class ExampleClassTest {
private final ExampleClass exampleClass = new ExampleClass();
#Test
public void exceptionIsNotThrownIfCoolObjHasNoErrors() throws Exception {
CoolObj coolObjMock = Mockito.mock(CoolObj.class);
PowerMockito.whenNew(CoolObj.class).withNoArguments().thenReturn(coolObjMock);
Mockito.when(coolObjMock.hasErrors()).thenReturn(false);
Assertions.assertThatCode(exampleClass::doSomething)
.doesNotThrowAnyException();
}
#Test
public void exceptionIsThrownIfCoolObjHasErrors() throws Exception {
CoolObj coolObjMock = Mockito.mock(CoolObj.class);
PowerMockito.whenNew(CoolObj.class).withNoArguments().thenReturn(coolObjMock);
Mockito.when(coolObjMock.hasErrors()).thenReturn(true);
Assertions.assertThatThrownBy(exampleClass::doSomething)
.isInstanceOf(RuntimeException.class);
}
}
Notes:
For more details, regarding code snippet in the answer take a look here.
Please, note that to work properly PowerMock and Mockito should have compatible versions.
I see that in this piece of code,
expect(myService.getAll(anyBoolean())).andReturn(objectList).anyTimes();
replay(scopeService);
That expect statement statement is throwing
IllegalStateException - missing behavior definition for the preceding method call:
myService.getAll(true)
Usage is: expect(a.foo()).andXXX()
I understand that if it throws this exception if I dont have the andReturn, or if I missed calling the replay, or if my object is not a mock. I have checked all of that and it is not the case! Can someone please let me know if something else could be wrong?
I have around a 50 expect/replays before and nothing has an issue.
You haven't provided more code so I assume that your code looks like this more or less:
import static org.easymock.EasyMock.*;
public class SomeTests {
static class Foo {
public List<Integer> getAll(boolean value) {
throw new RuntimeException();
}
}
public void someTestCase() {
Foo mock = createMock(Foo.class);
List<Integer> ret = Collections.singletonList(1);
expect(mock.getAll(anyBoolean())).andStubReturn(ret);
replay(mock);
mock.getAll(true); // returns a list with single element 1 rather than throwing an exception
}
}
my advice is that:
Check if myService instance is created by one of mocking methods
Reply should be called on myService as it switches mode of a mock
I figured out the issue. The issue was that the mock myService was being used to create the object that was being tested(since it has it as a member) and to create another mock object needed for the tests.
When I changed them to use two different myService(myService, myService1) it worked! I am not sure how that would help, but it did!
Is there any valid variant to test controller with the predicate in it?
#RestController
public class QueryLauncherController {
private QueryLauncherService queryLauncherService;
#Autowired
public QueryLauncherController(QueryLauncherService queryLauncherService) {
this.queryLauncherService = queryLauncherService;
}
#GetMapping("/queryLauncher/CMP")
public List<QueryLauncherDto> getLaunchersCompany(RTAdmin admin) {
return queryLauncherService.getLaunchersList(admin, QueryLauncher::getIsCompany);
}
}
I've tried to solve this issue using any(Predicate.class) but IDE highlights it as Unchecked assignment and it looks pretty dumb.
The code I've written for the test is:
public void returnCompanyLinks_When_companyRequested() throws Exception {
when(queryLauncherService.getLaunchersList(eq(user), any(Predicate.class))).thenReturn(queryLauncherDtos);
mockMvc.perform(get(QUERY_LAUNCHER_URL + CMP).session(mockSession))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(content().json(new Gson().toJson(queryLauncherDtos)));
}
The goal is to run method only in the case when QueryLauncher::getIsInvestor.
List<QueryLauncherDto> getLaunchersList(RTAdmin admin, Predicate<QueryLauncher> launcherType);
Are there any variants to do it?
I believe this will do what you want:
doReturn(queryLauncherDtos).when(queryLauncherService).getLaunchersList(eq(user),
ArgumentMatchers.<Predicate<QueryLauncher>>anyObject());
Some caveats:
I prefer the doReturn(value).when(mock).method form when specifying mocked functionality,
but it is easy enough to flip to the when(mock.method).thenReturn(value) form.
anyObject is deprecated in the latest version of Mockito.
It is still present,
but it is deprecated.
Edit: I read the 2.23 release Mockito documentation,
and it mentions that anyObject is just an alias for the any method.
The following should work and is not deprecated:
doReturn(queryLauncherDtos).when(queryLauncherService).
getLaunchersList(eq(user),
ArgumentMatchers.<Predicate<QueryLauncher>>any());
I have following code preparing mocks to test my service using Cassandra (I need to mock com.datastax.driver.core.ColumnDefinitions.Definition) :
#RunWith(PowerMockRunner.class)
public class TestMyClass{
private MyClass target;
#Before
public void setUp() throws Exception {
ColumnDefinitions mockColumnDefinitions=Mockito.mock(ColumnDefinitions.class);
Mockito.when(mockRow.getColumnDefinitions()).thenReturn(mockColumnDefinitions);
target= new MyClass();
Definition mockDef = Mockito.mock(Definition.class);
List<Definition> defList = new ArrayList<Definition>();
defList.add(mockDef);
Iterator mockIterator = Mockito.mock(Iterator.class);
Mockito.when(mockColumnDefinitions.iterator()).thenReturn(mockIterator);
Mockito.when(mockIterator.hasNext()).thenReturn(true, false);
Mockito.when(mockIterator.next()).thenReturn(mockDef);
Mockito.when(mockDef.getName()).thenReturn(NAME);
}
#Test
public void testMyMethod() throws Exception {
target.MyMethod();
}
}
Test execution goes fine this place, and I have this type of code in different places, so it should work.
Inside the service I am testing I have following code:
ColumnDefinitions colDef = row.getColumnDefinitions();
Iterator<Definition> defIterator = colDef.iterator();
while (defIterator.hasNext()) {
Definition def = defIterator.next();
String columnName = def.getName();
}
When I debug this code, I see, that both colDef and defIterator are mocked successfully. I see something like that in debug variables area:
Mock for Iterator, hashCode: 430126690
But after defIterator.next() invocation I see that though def is an object and not null, it doesn't show hashcode like for Iterator, instead I see this:
com.sun.jdi.InvocationException occurred invoking method.
And after invoking this string:
String columnName = def.getName();
I immediately get NullPointerException like if def is null.
What am I doing wrong?
Thanks.
EDIT 1 ________________________________________________________________________
I also tried to use PowerMockito with the same methods instead, the result is the same.
EDIT 2 ________________________________________________________________________
I added the whole test method code.
It is been a while since this question was created. I have faced this same problem few days ago and I have solved it in the following manner (I hope my proposed solution helps someone in the future):
First of all, I want to clarify that ColumnDefinition.Definition class is a public static nested class that has four private final fields and only has one constructor: Definition (String keyspace, String table, String name and DataType type) (for more details please refer to the ColumnDefinitions.Definition javadoc and ColumnDefinitions source code). Therefore this nested class could not be mocked by Mockito nor Powermock because of its final fields.
SOLUTION:
I had to create a real object, not a mocked one of the class ColumnDefinition.Definition using reflection, so you can initialise the mockDef object as follows:
Constructor<Definition> constructor = (Constructor<Definition>) Definition.class.getDeclaredConstructors()[0]; // as Definition only has one constructor, 0 will be passed as index
constructor.setAccessible(true);
Definition mockDef = constructor.newInstance("keyspace", "table", "name", null);
replacing this line of code in your snippet:
Definition mockDef = Mockito.mock(Definition.class);
Then the NullPointerException will never be thrown again when executing this line of code:
String columnName = def.getName();
I have a method where a parameter is marked with the #Nonnull annotation. The code which calls the method has to check whether the value is null. Rather than just a straight x != null check, it is calling a utility method on another class. (In the real code, the utility method also checks whether it is a blank String).
My problem is that Intellij Idea is showing an inspection warning on the Nonnull method call, saying that my variable "might be null". I know it cannot be null because of the utility method check - how can I tell the inspector that?
Since that is a bit abstract, here's a minimal example of what I mean:
package org.ethelred.ideatest;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* tests annotations
*/
public class AnnotationChecker
{
public static void main(String[] args)
{
String x = null;
if(args.length > 0)
{
x = args[0];
}
if(!isNull(x))
{
useObject(x);
}
if(x != null)
{
useObject(x);
}
}
public static boolean isNull(#CheckForNull Object o)
{
return o == null;
}
public static void useObject(#Nonnull Object o)
{
System.out.println(o);
}
}
This uses the JSR 305 annotations.
In this example, in the first call to useObject Intellij puts a warning on the x parameter saying "Argument 'x' might be null". In the second call, there is no warning.
In IDEA 13 very fancy feature was added, called Method Contracts. For example, you could have a method, that throws validation exception if it encounters null:
#Contract("null -> fail")
public static void validateNull(#Nullable final Object object) {
if (object == null) {
throw new ValidationException();
}
}
IDEA will analyze the annotation and won't show up warnings, if you call it before possible NPE:
validateNull(user);
user.setSomething("something"); // no warning
You have full documentation in IDEA's settings (just search for Method Contract). For this to work you need additional dependency on jetbrain's annotations jar:
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
</dependency>
With IDEA 12 you can configure the NotNull-check methods:
http://youtrack.jetbrains.com/issue/IDEA-35808#tab=Comments
I don't believe there's any way to resolve the warning with the code written as it is. I was hoping to find that IntelliJ supports a value for #SuppressWarnings that you could use on the useObject(x) statement, but according to this source it does not. You may just have to bite the bullet and change your code to something like the following:
if (x != null && !isBlank(x)) {
useObject(x);
}
Notice that I renamed the method isNull to isBlank since it is my understanding that the actual method you're calling that does the check for null checks other conditions as well.
I've dealt with this issue by using a static method to whitelist object calls:
/** Wrapper to mask NullPointerException warnings. */
private static <T> T _(T obj) {
return obj;
}
The method name is intended to not interfere with readability. Thus,
mObject.method();
becomes
_(mObject).method();
Furthermore, if a NullPointerException does occur it will still refer to the same line number.