Issue
Here is the class I want to test
@Component
public class PermissionCheck {
@Autowired
private MyEntityRepository myEntityRepository;
public boolean hasPermission(int myEntityID) {
MyEntity myEntity = myEntityRepository.findById(myEntityId);
return myEntity != null;
}
}
Here is the test class
@RunWith(SpringRunner.class)
public class PermissionCheckTests {
@MockBean
private MyEntityRepository myEntityRepository;
private PermissionCheck permissionCheck;
@Before
public void before() {
this.permissionCheck = new PermissionCheck();
}
@Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
And when I run this test I got
java.lang.NullPointerException
at PermissionCheck.hasPermission(PermissionCheck.java:line1)
at PermissionCheckTests.shouldHasPermission(PermissionCheckTests.java:line2)
In the above, line1 and line2 refer to these two lines
MyEntity myEntity = myEntityRepository.findById(myEntityId);
assertTrue(this.permissionCheck.hasPermission(0));
And using debugger I see that when entering PermissionCheck.hasPermission
from PermissionCheckTests.shouldHasPermission
, the repository field
@Autowired
private MyEntityRepository myEntityRepository;
is null.
I created these classes by referring to others existing codes, from different places, and without really understanding how to the annotations (partially due to running out of time), so if someone can tell me not only how to fix, but also why I'm wrong, I would really appreciate it!
Edit:
I made the change suggested by @Nikolas Charalambidis (thank you!), so my PermissionCheck
class now looks exactly like
@RunWith(SpringRunner.class)
public class PermissionCheckTests {
@Autowired // you need to autowire
private PermissionCheck permissionCheck; // and it uses @MockBean dependency
@MockBean // if no such @MockBean exists
private MyEntityRepository myEntityRepository; // the real implementation is used
@Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
But I then got the following exception
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'PermissionCheckTests':
Unsatisfied dependency expressed through field 'permissionCheck';
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'PermissionCheck' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true),
@org.springframework.beans.factory.annotation.Qualifier(value="")}
With a little bit search, from this SO answer, I feel like I should not @Autowired PermissionCheck
, since it is a class, not an interface.
Does that mean I have to create an interface, @Autowired it and let PermissionCheck
implement it? It seems redundant to me, since I don't see why I need such an interface. Is there a way to make it work without creating a new interface which I will solely use for this purpose?
Solution
The instance of class PermissionCheck
is not properly injected. The tests use the Spring container in the same way as the production code. The following line would not inject the myEntityRepository
.
this.permissionCheck = new PermissionCheck();
Spring: NullPointerException occurs when including a bean partly answers your question. You need to @Autowire
the thing.
The Spring test context is no different. As long as @MockBean
MyEntityRepository
exists as a mocked bean, the PermissionCheck
will be autowired in the standard way using the mocked class in precedence over the existing bean in the test scope.
@RunWith(SpringRunner.class)
public class PermissionCheckTests {
@Autowired // you need to autowire
private PermissionCheck permissionCheck; // and it uses @MockBean dependency
@MockBean // if no such @MockBean exists
private MyEntityRepository myEntityRepository; // the real implementation is used
@Test
public void shouldHasPermission() {
MyEntity myEntity = new MyEntity();
when(this.myEntityRepository.findById(any())).thenReturn(myEntity);
assertTrue(this.permissionCheck.hasPermission(0));
}
}
Answered By - Nikolas Charalambidis
Answer Checked By - Timothy Miller (JavaFixing Admin)