I have created a basic Rest API with a Controller, Service and Repository layer. I am now trying to write a unit test for the Service layer method that finds product by the product id. However, when I run the test it throws a NullPointerException in the line where I am trying to mock the repository findById() method.
The getProductById() method in the service layer is as follows:
public Product getProductById(String id){ return productRepository.findById(id).orElse(null); }
My test class for the service layer:
package com.product.Services;
import com.product.Entities.Product;
import com.product.Repositories.ProductRepository;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
class ProductServiceTest {
#InjectMocks
ProductService productService;
#Mock
ProductRepository productRepository;
#Test
void getProductById() throws Exception {
Product product = new Product();
product.setId("001");
product.setName("TV");
product.setPrice(999.99);
when(productRepository.findById("001").orElse(null)).thenReturn(product);
assertEquals(product,productService.getProductById("001"));
}
}
Attaching a picture of what the debugger shows:
The problem is that 2 different versions of JUnit are used here:
org.junit.jupiter.api.Test is from JUnit5, while
org.junit.runner.RunWith is from JUnit4.
RunWith does not exist anymore in JUnit5.
In this specific case, I would use JUnit4 - i.e. use the annotation org.junit.Test (instead of org.junit.jupiter.api.Test) to annotate your test.
How to use Mockito with JUnit5 provides other valid solutions.
JUnit 5 uses #ExtendWith(SpringExtension.class) annotation, can you change #RunWith annotation and try again?
You should mock the findById call, not the expression chained with orElse:
when(productRepository.findById("001")).thenReturn(product);
Related
Hi I am new to Spring Mongo testing. I know that using embedded db like flapdoodle ,fongo,mongo-java-server we can unit test the mongorepository.
But is the same possible using mockito?
If yes then which is better
Mockito is used for mocking calls to external dependencies. Utilities like flapdoodle are used to mock external dependencies (MongoDB in this case). It's just two different approaches.
You can mock methods of your repository classes to return some stub values instead of making calls to the database. Please see my example below for this case:
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import static org.mockito.Mockito.doReturn;
class FooRepositoryTest {
#Mock
private FooRepository repository;
#Test
void testGetById() {
doReturn(new Object()).when(repository).findById("id");
Object object = repository.getById("id");
}
}
You can mock your database (by just instantiating some other test-embedded database) and make actual calls to it in your tests. This is where flapdoodle comes in handy. In this case, you will be using your actual repositories/services/etc. Please see my example below:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
#DataMongoTest
#ExtendWith(SpringExtension.class)
class FooMongoRepositoryTest {
#Autowired
private FooRepository repository;
#Test
void testGetById() {
Object object = repository.getById("id");
}
}
But the second case won't be a true unit test, since it will be using an external dependency. At the same time, it won't be a true integration test, since your real application uses a real Mongo database and not some flapdoodle dependency.
Answering your question regarding what is better. It depends on the particular case. It's fully acceptable to use either of these options. It even makes sense to use both because it's just two different types of developer tests.
What else you may consider:
There is a nice library called testcontainers, which allows you to run real databases and other services in Docker containers. It even has a separate and preconfigured MongoDB Module
I'm working on a legacy project that have no tests. We want to add integration tests for our services.
The services communicates with other services and with the database as well.
I could handle to run junit tests so I can mock service calls and database calls to return whatever I want but I'm wondering if it's possible to run an actual integration test where it communicate with the actual dev database and with the other services in the project as well.
The beans are defined in xml files with some placeholders on it.
I'm looking for a direction on what to look for and if it is possible at all on this spring version.
Thanks!
To configure your test can you make first class SwitchCase and extend this in test class.
import java.util.Random;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class SwitchCase{
*** here you can have tour variables and do #Autowired in yours service
}
public class YoursTestClass extends SwitchCase{
#Test
public void save() throws Exception {
}
}
To do mocks you can use Mockito.
#WebMvcTest
#AutoConfigureMockMvc
public class YouClass {
#Autowired
MockMvc mock
MockHttpServelet request = MockMvcRequestBuilders
.post("http/....")
.contentTupe(**pass yout content type **);
mock.perform(request)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.jsonPath("id").value(7)
}
It is just an example to guide, but it would be good to take a look at the mockito documentation
I have a Spring Boot app which contains a Spring Data Jpa repository. I need to run a unit (or component?) test around this repository. I do not have a lot of experience with Spring Data Jpa.
Here's my test. It's trivially simple, and I cannot get it to pass.
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.junit.Assert.assertNotNull;
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
Here's the other relevant source code.
import com.fedex.dockmaintenancetool.webservice.types.Foo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long> {
}
and
import javax.persistence.Entity;
#Entity
public class Foo {
}
I am just trying to get a Jpa repo autowired into a test, and I can't. Clearly I'm misunderstanding some small nuance of how Spring Boot works. But even after going through some tutorials, I cannot figure out what I'm missing. Could anyone help me with this?
When you use the annotation #DataJpaTest , it means that you are trying to test only the repository layer. The annotation is used to test JPA repositories and is used in combination with #RunWith(SpringRunner.class) to enable populating the application context. The #DataJpaTest annotation disables full auto-configuration and applies only configuration relevant to JPA tests.So as #fap siggested use it like :
#RunWith(SpringRunner.class)
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
When you use the annotation #RunWith(SpringRunner.class) the SpringRunner provides support for loading a Spring ApplicationContext and having beans #Autowired into your test instance.
You're missing the #RunWith(SpringRunner.class) annotation that tells JUnit to actually start a Spring application for the test.
Your test class should look like
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertNotNull;
#RunWith(SpringRunner.class)
#DataJpaTest
public class FooRepositoryTest {
#Autowired
private FooRepository fooRepo;
#Test
public void notNull(){
assertNotNull(fooRepo);
}
}
The JUnit version used in the question is still JUnit 4.
Spring Boot 2.2.0 switches to JUnit5.
With JUnit5 you'll have to use #ExtendWith(SpringExtension.class) instead of #RunWith(SpringRunner.class). Since #JpaTest is already annotated with #ExtendsWith you don't have to actually include it though, see https://stackoverflow.com/a/65359510/4266296.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
"classpath:spring/integration-test.xml"
})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public class TestClass {
#Autowired
private Dao dao;
#Test
#Transactional
public void test1() {
}
}
Hello!
I'm trying to start single test method from Spring Tool Suite Debug configurations and I have an exception:
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=test1], {ExactMatcher:fDisplayName=test1(com.org.TestClass )], {LeadingIdentifierMatcher:fClassName=com.org.TestClass ,fLeadingIdentifier=test1]] from org.junit.internal.requests.ClassRequest#302e67
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:77)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:68)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:43)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
But since I remove #RunWith(SpringJUnit4ClassRunner.class) at this example the test method launched successfully.
I would appreciate any useful advice to eliminate this trouble.
Thank you.
I have spring version 3.1.1
Here are imports:
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.Test;
It is cause spring configuration specific works.
Spring created proxy instance for your class because of #Transactional annotation under your test method, and after that method doesn't contain #Test annotation.
Please see information about spring proxy there: https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring
Do you really need #Transactional annotation under your test method?
You can create class-helper only for testing, write #Transactional annotated methods there, inject it (annotation #Autowired) and use in your test class
I have the following test code:
package soundSystem;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class )
#ContextConfiguration(classes = CDPlayerConfig.class)
public class SonyCDPlayerTest {
#Autowired
private ICompactDisk cd;
#Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
This is a maven project, the problem is the exact same code would run in eclipse, but not in intellij.
I just can't find a way to resolve #RunWith
The #RunWith annotation has been replaced with #ExtendWith in JUnit 5.0 and above (which the latest spring version is now using).
Example:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = { SpringTestConfiguration.class })
public class GreetingsSpringTest {
// ...
}
Quoted from Baeldung:
Note that SpringExtension.class is provided by Spring 5 and integrates
the Spring TestContext Framework into JUnit 5.
Ref: https://www.baeldung.com/junit-5-runwith
Simple: your IDE is not configured to for unit testing.
In other words: you are missing all the JUnit related classes. You can see that all those JUnit imports are underlined; as IntelliJ simply doesn't know about the JARs that contain the corresponding classes.
See here on how to fix that.