Unit testing Spring throws BeanNotOfRequiredTypeException - java

Running a test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
This error gets thrown only with unit tests, the program itself works.
My Interface
public interface MyService {
public String testMethod();
}
My Implementation
#Service
public class MyServiceImpl implements MyService{
#Autowired
private TransactionRepository transactionRepo;
#Autowired
private AccountRepository accountRepository;
#Autowired
private BankAccountStatementFactory baStatementFactory;
public String myMethod() {
return "Run My Method";
}
}
My Unit Test
#RunWith(SpringRunner.class)
#SpringBootTest
public class DeleteMeTest{
#Mock
private TransactionRepository transactionRepo;
#Mock
private AccountRepository accountRepository;
#Mock
private BankAccountStatementFactory baStatementFactory;
#InjectMocks
#Resource
MyServiceImpl myService;
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
myService.myMethod();
System.out.println("My Unit Test");
}
}
Running this test class throws the following exception:
BeanNotOfRequiredTypeException: Bean named 'myServiceImpl' is expected to be of type 'MyServiceImpl' but was actually of type 'com.sun.proxy.$Proxy139'
A solution here is to inject the Interface, not the implementation into the unit test but this will not allow me to inject mocks.
Thats because an implementation is required with the #InjectMocks annotation. When I try to inject mocks into the interface I get the following exception:
Cannot instantiate #InjectMocks field named 'myService'! Cause: the type 'MyService' is an interface.
Just to be clear, all this worked in the beginning and went bad once I repackaged my classes. That could be the cause but not 100% sure.
Any hints on what might cause this BeanNotOfRequiredTypeException?
Thanks!

Since it's a unit test, you don't need Spring IMHO.
Simply initialize the tested class this way:
#InjectMocks
MyServiceImpl myService = new MyServiceImpl();
You can also remove these annotations:
#RunWith(SpringRunner.class)
#SpringBootTest

If you really need to use Spring (the reason for using Spring in the unit test is not clear from your post), you can try to unproxy the bean:
Have a separate declaration for the proxy and the bean:
#Resource
MyServiceImpl proxy;
#InjectMocks
MyServiceImpl myService;
Then initialize them in setUp():
#org.junit.Before
public void setUp() throws Exception {
// Initialize mocks created above
myService = (MyServiceImpl)((TargetSource) proxy).getTarget();
MockitoAnnotations.initMocks(this);
}

Related

Java/Spring: initMocks a class which got #Value from yml file

I am writing unit tests on a Service class like below :
#Component
#Profile({"default", "dev"})
public class MyService {
#Value("${my.property}")
private String property;
private OtherService otherService;
public void MyMethod() {
String myVar = otherService.method();
return String.format("MyVar %s & MyProperty %s", myVar, property);
}
}
My current test class for this test is like this:
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#Mock
private OtherService otherService;
#InjectMocks
private MyService myService;
#BeforeEach()
public void init() {
MockitoAnnotations.initMocks(this);
when(otherService.method()).thenReturn("a string");
}
#Test
public void shouldReturnException() {
final Exception exception = assertThrows(ApiErrorException.class,
() -> myService.myMethod(var));
assertThat(exception).hasMessage("here the message");
verify(otherService, never()).method();
}
}
With this two classes, I have an application.yml & application-dev.yml to set my.property.
I want to get the property from the application-dev file during my tests execution.
But, with #InjectMocks, property is null. Whereas, using #Autowired in place of/with #InjectMocks, the property variable is set with the value present in file.
Problem, using Autowired with/in place of InjectMock results in the otherService variable being initialized, so no mock is created.
How can I still use Mockito, while having the property variable set with the value in the file?
I saw about ReflectionTestUtils.setField, but using it mean having no use of a yml file (which i am not fan).
Have a nice day
With the help of #Deadpool, the tests can use the values written in the application.yml file.
But, using #MockBean and #Autowired, the tests get a behavior I do not understand.
Example:
I test that a method return an Exception, and I verify that others methods are not called after the exception was catch: verify(otherService, never()).otherMethod();
Writing this line returns the following error org.mockito.exceptions.verification.NeverWantedButInvoked.
The initial exception is correctly caught, but the test does not seem to acknowledge that no other services must be called.
#SpringBootTest is used for integration testing which loads the ApplicationContext that will be utilized for test environment
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
Since you are generating integration environment using #SpringBootTest you need to mock the bean using #MockBean annotation
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#MockBean
private OtherService otherService;
#Autowire
private MyService myService;
#BeforeEach
public void init() {
when(otherService.method()).thenReturn("a string");
}
}
I suggest you change your MyService class to accept the OtherService either via the constructor or via setter. Something like this:
#Component
#Profile({"default", "dev"})
public class MyService {
#Value("${my.property}")
private String property;
private OtherService otherService;
public MyService(OtherService otherService) {
this.otherService = otherService
}
public void MyMethod() {
String myVar = otherService.method();
return String.format("MyVar %s & MyProperty %s", myVar, property);
}
}
And then you do your test like this:
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#Mock
private OtherService otherService;
#InjectMocks
#Autowired
private MyService myService;
#BeforeEach()
public void init() {
MockitoAnnotations.initMocks(this);
when(otherService.method()).thenReturn("a string");
}
}

Can't inject Bean (child of #Spy injected bean) in my Test

When I'm making a Test, I can't get injected a property of one of the injected beans (with #Spy). I am using Mockito to test.
I tried using #Mock, #Spy, #SpyBean and #InjectMocks in this Bean on my test but I can't get it injected.
#RunWith(MockitoJUnitRunner.class)
public class MyTest{
#InjectMocks private MyService = new myService();
#Spy private MyFirtsDepen firstDepen;
#Autowired #Spy private ChildDepen childDepen;
... More mocks and tests
}
#Service
public class MyService {
#Autowired private MyFirstDepen firstDepen;
....
}
#Mapper
public class MyFirstDepen {
#Autowired private ChildDepen childDepen;
....
}
#Component
public class ChildDepen {
...
}
When my test use firstDepen is working great, but when firstDepen uses childDepend always get Nullpointer. How can I inject this property in my test?
Since your MyFirtsDepen is a mock, there is no way to inject anything to it. Configure mock to return another mock.
when(firstDepen.getChildDepen()).doReturn(childDepen);

unit test a interface implementation with mock in Spring Boot

I'm trying to write a simple unit test for a service in Spring Boot.
The service calls a method on a repository which returns an instance of User.
I'm trying to mock the repository, because I want to test only the service.
So, the code for Repository:
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
Service interface:
public interface UserService {
#Async
CompletableFuture<User> findByEmail(String email) throws InterruptedException;
}
Service implementation:
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterruptedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Unit Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#InjectMocks
UserService userService;
#Mock
UserRepository mockUserRepository;
#Before
public void setUp() {
MockitoAnnotations.initMock(this);
}
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
When I run this test, I got a MockitoException:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'userService'.
...
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
Instead of using the interface, I tried to use the real implementation; changing the test like this:
#InjectMocks
UserServiceImpl userService;
Now, the test passes with success, but this don't appear be right (at least for me).
I like to test the UserService that Spring Boot is using (suppose that in a new version of my system, I implement a new UserServicePostgreSQLImpl - now I'm using MongoDB).
(edit: see the bottom edit in the question)
I changed the Unit Test as follows:
#Autowired
#InjectMocks
UserService userService;
but now I got a test failure:
Expected :model.User#383caf89
Actual :null
For some reason, when I use #Autowired, the UserRepository mock doesn't work.
If I change the emailTest to use a real email in my database,
the test passes.
When I use #Autowired,
the test is using the real UserRepository and not a Mock version of UserRepository.
Any help?
Edit: looking at the answers from #msfoster and #dunni, and thinking better, I believe that the correct way is to test every implementation (in my example, use UserServiceImpl userService).
In order for your UserServiceImpl to be autowired when annotating it with #InjectMocks then it needs to registered as a Spring bean itself. You can do this most simply by annotating your UserServiceImpl class with #Service.
This will ensure it is picked up by the component scan in your Spring boot configuration. (As long as the scan includes the package your service class is in!)
You are running your tests with SpringRunner but for mocks you don't really need spring context. Try following code
// Using mockito runner
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
// Mockito will auto inject mockUserRepository mock to userService via constructor injection
#InjectMocks
UserService userService;
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
This is just a variation on the #Yogesh Badke answer.
Although you are using spring at runtime,
there is no need to use spring during the unit test.
Instead,
you can mock all the dependencies and set them to the mocks during test setup
(using reflection or setters, if you have them).
Here is some example code:
import org.springframework.test.util.ReflectionTestUtils;
public class TestUserService
{
private static final String VALUE_EMAIL = "test email value";
private UserService classToTest;
#Mock
private User mockUser;
#Mock
private UserRepository mockUserRepository;
#Before
public void beforeTest()
{
MockitoAnnotations.initMock(this);
classToTest = new UserService();
doReturn(mockUser).when(mockUserRepository).findByEmail(VALUE_EMAIL);
ReflectionTestUtils.setField(
classToTest,
"userRepository",
mockUserRepository);
}
#Test
public void findByEmail_goodEmailInput_returnsCorrectUser()
{
final User actualResult;
actualResult = classToTest.findByEmail(VALUE_EMAIL);
assertSame(
mockUser,
actualResult);
}
}
If interface is implemented by more than one class, then use the qualifier name (example below) in Junit beans.xml file to run the respective Junit test case.
Example:
#Autowired
#Qualifier("animal")
private Animal animals;
In Junit beans.xml
<bean id="animal" class="com.example.abc.Lion"/>
where Lion is the implementation class for the Interface Animal.
You need to #InjectMocks for the implementation class. Not the interface class.
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
#InjectMocks
UserServiceImpl userServiceImpl; ------> This is important
}

Spring #Autowired constructor causes #Value to return null when instantiated in test class

I'm using an autowired constructor in a service that when instantiated in the test class causes the #Value annotations to return null. Autowiring the dependencies directly solves the problem but the project follows the convention of using constructor based autowiring. My understanding is that instantiating the service in the test class is not creating it from the Spring IoC container which causes #Value to return null. Is there a way to create the service from the IoC container using constructor based autowiring without having to directly access the application context?
Example Service:
#Component
public class UpdateService {
#Value("${update.success.table}")
private String successTable;
#Value("${update.failed.table}")
private String failedTable;
private UserService userService
#Autowired
public UpdateService(UserService userService) {
this.userService = userService;
}
}
Example Test Service:
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class})
#WebAppConfiguration
public class UpdateServiceTest {
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService = new UpdateService(mockUserService);
}
}
To make #Value work updateService should be inside of spring context.
The best practice for spring framework integration tests is to include application context in test context and autowiring test source in test:
...
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
...
Mock userService
Option with changing userService to protected and considering that test and source classes are in same package.
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService.userService = mockUserService;
}
Option with reflection with Whitebox:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Whitebox.setInternalState(updateService, 'userService', mockUserService);
}
The #Value is filled by a property placeholder configurer which is a post processor in the spring context. As your UpdateService is not part of the context it is not processed.
Your setup looks a little like a unclear mixture of unit and integration test. For a unit tests you will not need a spring context at all . Simply make the #Value annotated members package protected and set them or use ReflectionTestUtils.setField() (both shown):
public class UpdateServiceTest {
#InjectMocks
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(updateService, "successTable", "my_success");
updateService.failedTable = "my_failures";
}
}
For an integration test all wiring should be done by spring.
For this I added a inner config class providing the mock user service (the #Primary is only for the case you have any other user service in your context) and the mock is stored in a static member here to have simple access to the mock from the tests afterwards.
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class, UpdateServiceTest.TestAddOn.class})
#WebAppConfiguration
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
private static UserService mockUserService;
static class TestAddOn {
#Bean
#Primary
UserService updateService() {
mockUserService = Mockito.mock(UserService.class);
return mockUserService;
}
}
}

Context initialization issue for controller Unit testing with Spring 3.2 and Mockito

I'm trying to provide a clean Unit Test for a Controller of mine. This Controller has a Service as dependency and this Serviceh has a Datasource as dependency.
The test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class ContentActionWebServiceControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private MyService myService;
#Test
public void getRequestActionList() throws Exception {
when(...)
perform(...);
verify(...);
}
#Configuration
#ImportResource("...")
static class MyTestConfiguration {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
}
}
And the MyService is something like
#Service
public class MyService {
#Autowired
private MyDataSource myDatasource;
...
}
Because MyService as an Autowired property MyDataSource, the context isn't initialized because it doesn't find any MyDataSource type for satisfying the #Autowired annotation of MyService. But why does it ever try to resolve this annotation? Is this is a mock?
Mockito does use cglib to create a new child class of MyService (and override all methods with mock methods).
But still, the dependencies of the parent will be injected, because this is how Spring does it's job:
if you have a parent class with some #Autowired fields, and a child class that inherits from this parent class, then Spring will inject the #Autowired fields of the parent when instantiating the child. I guess it's the same behavior in your case.
If you use an interface for MyService, then your problem will be solved.
If it's supposed to be a unit test (and not an integration test) you don't even need to use Spring, you can do it all with JUnit+Mockito. Rather than #Autowireing dependencies from Spring context, you can simply create mocks of the support objects (via #Mock) and inject them to the testee (via #InjectMocks). I believe your code could be simplified to something (conceptually) like this:
#RunWith(MockitoJUnitRunner.class)
public class ContentActionWebServiceControllerTest {
#Mock
private Service mockServiceUsedByController;
#InjectMocks
private YourController testee;
#Test
public void getRequestActionList() throws Exception {
assertFalse(testee.getRequestActionList().isEmpty());
// etc.
}
}

Categories