I want to know how to write Junit test for Spring Repository classes.
As example :
class-AccountMoveActionDet
Jpa interface-AccountMoveActionDetlJpaRepository
And I want to test this repository class work or not.Spring Jpa support some methods like
List findAll();
deleteAll();
I wrote a class just like below:
#RunWith(SpringJUnit4ClassRunner.class)
public class AccountTypeMovementJpaRepositoryTest extends AbstractJpaTest {
#Autowired
AccountTypeMovementJpaRepository accountTypeMovementJpaRepository;
#Override
public void test() {
executeSqlScript("/fixtures/accountTypeMovementJpa.sql");
assertEquals("accountTypeMovementJpaRepository Test", accountTypeMovementJpaRepository.findAll().size(),
JdbcTestUtils.countRowsInTable(getJdbcTemplate(), "COF5REP"));
}
}
Error creating bean with name
'com.gayan.cmp.jparepositories.test.AccountTypeMovementJpaRepositoryTest':
Please help me to resolve this.
If you use spring-boot 1.4 and above the best place to start, Testing the JPA slice :
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTests {
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void findByUsernameShouldReturnUser() {
this.entityManager.persist(new User("sboot", "123"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("123");
}
}
Related
I'm new to Java development so sorry in advance if I'm not using the appropriate terms.
Whenever I run a test on a class that needs to save something in my database, I face a NullPointerException on the Autowired repository.
I use Junit4, here are code snippets :
application-test.properties
spring.datasource.url=jdbc:tc:mysql:8.0.29://localhost:3306/MYSERVICE
MyService.java
#Component
class MyService {
#Autowired MyRepository myRepository;
public void mainFunction() {
myRepository.saveSomething();
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<T, Long> {
void saveSomething();
}
MyServiceTest.java
public class myServiceTest extends TestConfiguration {
#Rule
public MySQLContainer mysql = new MySQLContainer();
#InjectMocks MyService myService;
#Test
public void mainFunctionTest() {
myService.mainFunction()
}
}
MyServiceTestApplication.java
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class MyServiceTestApplication{
}
TestConfiguration.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyServiceTestApplication.class)
#ActiveProfiles("test")
public abstract class TestConfiguration {
}
When I run the test in debug mode, I can see that myRepository is null
Any help would be highly appreciated
Thanks :)
Edit 01/08/2022 : Add #Component on MyService
Edit 01/08/2022 (2) : Add MyServiceTestApplication.java and TestConfiguration.java
It seems, you forgot to annotate the class MyService with #Service.
With this annotation being made at that class, the Spring framework will recognize it:
This annotation serves as a specialization of #Component, allowing for
implementation classes to be autodetected through classpath scanning.
Given that the rest of the configuration is working, the #Autowired dependency injection mechanism will hereby provide you with an instance of the #Repository you requested, at runtime, here your test setup.
I have a service
#Service
public class ShopServiceImpl implements ShopService {
private ShopRepository shopRepository;
public ShopServiceImpl (ShopRepository shopRepository) {
this.shopRepository= shopRepository;
}
...
}
and I want to write test with mocked Mongo database to test the CRUD methods.
#RunWith(MockitoJUnitRunner.class)
public class GetShopServiceTest {
#Mock
private ShopRepository ShopRepository;
#InjectMocks
private ShopServiceImpl shopService;
#Test
public void randomTest() {
...
Shop shopSaved = shopService.save(shop);
List<Shop> shopsRetrieved = shopService.listShops();
}
}
But this mocked service does not save or retrieve saved record. I do not have any configurations that relate to mocked mongo db, but tried them and they didin't work. What to do?
You can use #DataMongoTest.
Sample Implementation
My goal is to use an in-memory database for these unit tests, and those dependancies are listed as:
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")
So that the repository instance actually interacts with a DB, and I dont just mock return values.
The problem is that when I run my unit test, the repository instance inside the service instance is null.
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
This is the console output when running my unit test:
null
java.lang.NullPointerException
at com.my.MyService.findAll(MyService.java:20)
at com.my.MyTest.testMy(MyTest.java:23)
My unit test class:
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
My service class:
#Service
public class MyService {
#Autowired
MyRepository myRepository;
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
My repository class:
#Repository
public interface MyRepository extends CrudRepository<MyEntity, Long> {
}
My entity class:
#Entity
public class MyEntity {
#Id
#GeneratedValue
public Long id;
}
Why is that? Am I missing some annotation on the unit test class to initialise the repository instance?
Basically yes :)
You need to initialise a Spring Context by Annotating your Testclass with #SpringBootTest
The other Problem you have is that you create your MyService Object manually.
By doing so SpringBoot has no chance to inject any Bean for you. You can fix this by simply injecting your MyService in your Testclass. Your Code should look something like this:
#SpringBootTest
public class MyTest {
#Autowired
private MyService myService;
#Test
void testMy() {
int size = myService.findAll().size();
assertEquals(0, size);
}
}
To use #MockBean annotation, you have to use SpringRunner to run the test. Use #RunWith Annotation on top of your test class and pass SpringRunner.class.
#RunWith(SpringRunner.class)
public class MyTest {
#MockBean
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService();
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
The problem here is your service implementation. Using #Autowired to inject the dependency will work when you run the whole app, but it do not allow you to inject a custom dependency when you'll need it, and a good example of this is testing.
Change your service implementation to:
#Service
public class MyService {
private MyRepository myRepository;
public MyService(MyRepository myRepository){
this.myRepository = myRepository;
}
public List<MyEntity> findAll() {
System.out.println(myRepository); // null
return (List<MyEntity>) myRepository.findAll(); // throws NullPointerException
}
#Transactional
public MyEntity create(MyEntity myEntity) {
myRepository.save(myEntity);
return myEntity;
}
}
This constructor will be called by spring. Then change your test to:
public class MyTest {
#Mock
MyRepository myRepository;
#Test
void testMy() {
MyService myService = new MyService(myRepository);
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
Note I have replaced #MockBean to #Mock as the previous annotation is for injecting a mock bean into the spring context, which is not needed if you're doing unit testing. If you want to boot spring context (which I would not recommend you) you need to configure your test class with #SpringBootTest or some of the other available alternatives. That will convert your test into an integration test.
PD: This test will not work if you don't provide a mock to myRepository.findAll(). Mockito default behaviour is to return null, but you're expecting it to return 0, so you'll need to do something like given(myRepository.findAll()).willReturn(0).
I believe you wish to write an integration test. Here you could remove the MockBean annotation and simply autowire your repository. Also, run with The SpringRunner class.
#RunWith(SpringRunner.class)
public class MyTest {
#Autowired
MyRepository myRepository;
#Autowired
MyService myService
#Test
void testMy() {
int size = myService.findAll().size();
Assertions.assertEquals(0, size);
}
}
This should work
I'm learning unit testing with Spring Boot. I've created a stub object from Spring Data repository class using Mockito. All works fine, here's a code:
#SpringBootTest
class UserServiceTest {
#MockBean
private UserRepository userRepository;
#BeforeAll
public void configureMock() {
User user = new User("mishenev",
"Dmitrii",
"Mishenev",
"123",
"mishenev.8#gmailcom",
new UserSettings());
when(userRepository.findById(anyLong())).thenReturn(Optional.of(user));
when(userRepository.findUserByUserName(anyString())).thenReturn(user);
// .. Tests
}
But it's kind of boilerplate style to use this #BeforeAll test repository configuration for each class, if we don't need different stub behaviour. Then i tried to use #TestConfiguration
#TestConfiguration
public class SpringRepositoryTestConfiguration {
#Bean
public UserRepository userRepository () {
UserRepository userRepository = Mockito.mock(UserRepository.class);
// Configuring mock's behaviour
return userRepository;
}
}
After that i used
#SpringBootTest(classes = SpringRepositoryTestConfiguration.class)
But i can't using #MockBean to autowire repository in UserService now.
I want to understand is it possible to take out and use all test Repository type stubs in one configuration class. Thank you in advance!
Just use this
class TestConfig {
#Bean
UserRepository userRepository() {
final UserRepository repo = mock(UserRepository.class);
.. do mocking ..
return repo;
}
}
Then you can just #Import(TestConfig.class) where you need it
I have any issue in my unit test where I have something along the lines of this. The mock injection get overridden on the someService if the blargh function is annotated with Transactional. If I remove the Transactional the mock stays there. From watching the code it appears that Spring lazily loads the services when a function in the service is annotated with transactinal, but eagerly loads the services when it isn't. This overrides the mock I injected.
Is there a better way to do this?
#Component
public class SomeTests
{
#Autowired
private SomeService someService;
#Test
#Transactional
public void test(){
FooBar fooBarMock = mock(FooBar.class);
ReflectionTestUtils.setField(someService, "fooBar", fooBarMock);
}
}
#Service
public class someService
{
#Autowired FooBar foobar;
#Transactional // <-- this causes the mocked item to be overridden
public void blargh()
{
fooBar.doStuff();
}
}
Probably you could try to implement your test in the following way:
#Component
#RunWith(MockitoJUnitRunner.class)
public class SomeTests
{
#Mock private FooBar foobar;
#InjectMocks private final SomeService someService = new SomeService();
#Test
#Transactional
public void test(){
when(fooBar.doStuff()).then....;
someService.blargh() .....
}
}
I could not try it right now as don't have your config and related code. But this is one of the common way to test the service logic.
Use the Spring #Profile functionality - beans can be associated to a certain group, and the group can be activated or deactivated via annotations.
Check this blog post and the documentation for more detailed instructions, this is an example of how to define production services and two groups of mock services:
#Configuration
#Profile("production")
public static class ProductionConfig {
#Bean
public InvoiceService realInvoiceService() {
...
}
...
}
#Configuration
#Profile("testServices")
public static class TestConfiguration {
#Bean
public InvoiceService mockedInvoiceService() {
...
}
...
}
#Configuration
#Profile("otherTestServices")
public static class OtherTestConfiguration {
#Bean
public InvoiceService otherMockedInvoiceService() {
...
}
...
}
And this is how to use them in the tests:
#ActiveProfiles("testServices")
public class MyTest extends SpringContextTestCase {
#Autowired
private MyService mockedService;
// ...
}
#ActiveProfiles("otherTestServices")
public class MyOtherTest extends SpringContextTestCase {
#Autowired
private MyService myOtherMockedService;
// ...
}
I have the exact same problem and I solve it by using Mockito.any() for the arguments
eg:
when(transactionalService.validateProduct(id)).thenReturn("")
=> when(transactionalService.validateProduct(Mockito.any())).thenReturn("")