I am trying to test one test case using controller class with service interface and interface impl class. But it's always failing and returning either null pointer exception or not found custom exception even mocking the method to return mock values.
Controller.class
#RestController
public class RestAPIController {
#Autowired
RestAPIService restAPIService;
#PostMapping(path = "list/{id}")
public ResponseEntity<List<Employee>> findByID(#PathVariable(value = "id") String id)
throws RestAPIException {
List<Employee> list = restAPIService.findByID(id);
if (list == null || list.isEmpty()) {
throw new IDNotFoundException(id);
}
return new ResponseEntity<>(list, HttpStatus.OK);
}
Service Interface:
#Service
public interface RestAPIService {
public List<Employee> findByID(#Param("id") String id) throws RestAPIException;
}
Service impl class:
#Service
public class RestAPIServiceImpl implements RestAPIService {
#Override
public List<Employee> findByID(String id) throws RestAPIException {
return serviceUtils.transformationObj(repository.findByID(id));
}
}
Test Class
#Mock
RestAPIService restAPIServiceImpl;
#InjectMocks
RestAPIController restAPIController;
#Test
public void testListByIDController() throws RestAPIException {
Mockito.when(restAPIServiceImpl.findByID("1")).thenReturn(baseDTOData());
ResponseEntity<List<Employee>> expectedList = restAPIController.findByID("1");
assertEquals(1, expectedList.getBody().size());
}
private List<Employee> baseDTOData() {
List<Employee> list = new ArrayList<>();
Employee e = new Employee();
e.setId("2");
list.add(e);
return list;
}
pom.xml (using JUnit5)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- exclude junit 4 -->
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito extention -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
The above code is failing and enters into IDNotFoundException but mock return is not working.
Can anyone please help on this.
Related
I am trying to implement a reactive architecture to my Spring+MongoDB+GraphQL app, have followed some tutorials on that, but whenever I return a Publisher<Page> from my GraphQLQueryResolver - I get this error:
2022-11-17 09:18:56.316 ERROR 14166 --- [nio-8081-exec-1]
g.servlet.DefaultGraphQLErrorHandler : Error executing query
(ExceptionWhileDataFetching): Exception while fetching data (/getPages/id) : Expected
source object to be an instance of 'com.example.jobscraperspringserver.types.Page' but
instead got 'reactor.core.publisher.FluxJust'
I've played with the returned object (tried Flux<Page>, Publisher<List<Page>> and other configurations) as well as a type in the schema.graphqls, but none of them work. Besides, that's what supposed to be working in the majority of tutorials as well as some other SO issues, so I suppose that's either a bug somewhere in my code or an architectural flaw (i.e. some faulty dependency that doesn't support the type convertion).
Here is my code:
PageQuery.java
#Component
public class PageQuery implements GraphQLQueryResolver {
#Autowired
PageService pageService;
public Publisher<Page> getPages() {
return pageService.getPages();
}
}
PageService.java
#Service
public class PageService {
public Flux<Page> getPages() {
return Flux.just(new Page(12));
}
}
schema.graphqls
type Page {
id: Int!
host: String
}
type Query {
getPages: Page
}
pom.xml
...
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- GraphQL -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.2.4</version>
</dependency>
<!-- GraphQL subscriptions -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
</dependency>
<!-- h2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<!-- WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
...
UPDATE
Apparently com.graphql-java doesn't handle WebFlux, so I've replaced
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
with
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>1.0.0-M3</version>
</dependency>
but now I am getting a null instead of a Page...
UPDATE 2
I've also tried the
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>11.1.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-webflux</artifactId>
<version>11.1.0</version>
</dependency>
but got the same result. I figured it supports a CompletableFuture<List<Page>>, but this is still blocking and I've seen multiple tutorials on returning Mono/Flux from a graphql resolver...
UPDATE 3
I've updated the all the Spring modules' versions to 2.6.3 to be consistent and I've also removed the spring-boot-starter-web dependency, as the spring-boot-starter-webflux should be sufficient; but neither of those fixes have solved my issue.
UPDATE 4
This is the entity I want to return wrapped in a Flux:
#Document("pages")
public class Page {
#Id
private int id;
private String host;
private String path;
private String jobAnchorSelector;
private String jobLinkContains;
private int numberOfPages;
private int interval;
private Date lastScrapePerformed;
private String userUuid;
public Page() {
}
public Page(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getJobAnchorSelector() {
return jobAnchorSelector;
}
public void setJobAnchorSelector(String jobAnchorSelector) {
this.jobAnchorSelector = jobAnchorSelector;
}
public String getJobLinkContains() {
return jobLinkContains;
}
public void setJobLinkContains(String jobLinkContains) {
this.jobLinkContains = jobLinkContains;
}
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
}
public String toString() {
return "Page [" + host + path + "]";
}
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public Date getLastScrapePerformed() {
return lastScrapePerformed;
}
public void setLastScrapePerformed(Date lastScrapePerformed) {
this.lastScrapePerformed = lastScrapePerformed;
}
public String getUserUuid() {
return userUuid;
}
public void setUserUuid(String userUuid) {
this.userUuid = userUuid;
}
}
It's a MongoDB document and I'm using a ReactiveMongoRepository to fetch that, however for the sake of simplifying this issue I was trying to return just a Flux.just(new Page(2)).
First of all, if you're using Spring Boot, I would recommend that you use spring-boot-starter-graphql instead of graphql-spring-boot-starter, and you need to add spring-boot-starter-webflux to implement reactive data fetchers. Also if you want to return Flux, you should define returned type as an array in the schema (e.g. getPages: [Page!]!), otherwise, you should return Mono.
This will allow you to implement your query like this:
#Controller
public class PageController {
// ...
#QueryMapping
public Flux<Page> getPages() {
return pageService.getPages();
}
}
But you should note that by default Mono and Flux are adapted to a CompletableFuture where Flux values are aggregated and turned into a List. If you want to stream responses, then you should also use GraphQL subscription instead of a query.
I am using Spring Data Mongodb and Embeded mongoDB to persist the data.
Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
</dependency>
applicaiton.properties
spring.data.mongodb.port=2019
spring.data.mongodb.database=testdb
spring.data.mongodb.host=localhost
Main class:
#SpringBootApplication
#EnableAutoConfiguration
#EnableMongoRepositories(basePackages = { "com.koka.mongotest.repo"})
public class MongotestApplication {
public static void main(String[] args) {
SpringApplication.run(MongotestApplication.class, args);
}
}
Enity or Domain class :
#Document
public class Person
{
#Indexed
private String personId;
private String name;
//getter and setters
}
Repo:
#Repository
public interface PersonRepo extends MongoRepository { //No Custom emthod}
Service layer :
#Service
public class ServiceImpl {
#Autowired
PresonRepo repo;
public void saveJon(Person p )
{
repo.save(p);
}
}
but Int DB its getting saved as
{"_id":{"$oid":"60e18f9d7eb50d70b56b543f"},"_class":"com.koka.mongotest.entity.Person"}
But that person Object hold info which is not being saved. Please advise on this,
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
Class for which I m writing Junits:
public class AImpl implements AInterface {
public String method1(String id) throws Exception {
String s = Service.Factory.getInstance().generate(id);
return s;
}
}
Interface to be instantiated using its Inner class:
public interface Service {
String generate(String var1) throws Exception;
public static final class Factory {
private static Service instance = null;
public Factory() {
}
public static final Service getInstance() {
if (instance == null) {
instance = (Service)EnterpriseConfiguration.getInstance().loadImplementation(Service.class);
}
return instance;
}
}
}
I have tried powerMockito but it is not working.
#Test
public void generateTest() throws Exception {
Service.Factory innerClassMock = mock(Service.Factory.class);
String id= "id";
whenNew(Service.Factory.class).withArguments(anyString()).thenReturn(innerClassMock);
whenNew(innerClassMock.getInstance().generate("hjgh")).withAnyArguments().thenReturn(id);
id= AImpl.generate("hjgh");
Assert.assertEquals("id", id);
}
If I understand well your not cleary code you need this junit:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Service.Factory.class})
public class AImplTest {
private Service serviceMock;
#Before
public void setUp() {
PowerMockito.mockStatic(Service.Factory.class);
serviceMock = Mockito.mock(Service.class);
PowerMockito.when(Service.Factory.getInstance()).thenReturn(serviceMock);
}
#Test
public void generateTest() throws Exception {
Mockito.doReturn("mockid").when(serviceMock).generate(Mockito.anyString());
Assert.assertEquals("mockid", new AImpl().method1("aaa"));
}
}
Here my dependencies:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-core -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
Having said that I would change your Service.Factory class: here you need a private constructor in order to implement correctly the singleton pattern.
I have created a springboot project splitted into three maven modules, the domain layer, the core layer (contains persistence and business logic) and the web layer. I try to unit test my repository ProductRepository (located in the core layer)
#RunWith(SpringRunner.class) // provide bridge between SpringBoot test features and JUnit, for usage of springboot tsting features
#DataJpaTest
class ProductRepositoryTest {
#Autowired
private TestEntityManager em;
#Autowired
private ProductRepository repository;
#Test
void shouldReturnProduct() {
// given
Product p = Product.builder().id(1).designation("Test").reference("TEST").unitPrice(150).build();
this.em.persistAndFlush(p);
// when
Product found = repository.findByReference(p.getReference());
// then
assertThat(found.getReference()).isEqualTo(p.getReference());
}
}
But the repository is always instanciated to null. I run this test as JUnit Test in eclipse and i got a nullpointerexception.
Here is my pom.xml file
<dependencies>
<!--<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
You say you try to unit test the controller but you use #RunWith(SpringRunner.class), this is used for integration tests. This annotation starts the complete application. And you just want to test the repository. What you can do is create an abstract DAO which you can implement in your unit tests.
public abstract class AbstractRepository<RepositoryType> {
RepositoryType repository;
private DBI dbi;
protected abstract RepositoryType createRepository(final DBI dbi);
#Before
public final void setUpDataSource() throws Exception {
final JdbcDataSource jdbcDataSource = new JdbcDataSource();
// DB_CLOSE_DELAY=-1 ==> h2 will keep its content as long as the vm lives otherwise the content of the database
// is lost at the moment the last connection is closed.
jdbcDataSource
.setURL("jdbc:h2:mem:play;MODE=MySQL;DB_CLOSE_DELAY=-1L;INIT=RUNSCRIPT FROM 'classpath:path/to/file/init_test.sql';");
final Flyway flyway = new Flyway();
flyway.setDataSource(jdbcDataSource);
flyway.setLocations("/path/to/locations");
flyway.migrate();
dbi = new DBI(jdbcDataSource);
runDbScript("/data.sql");
repository = createRepository(dbi);
}
private void runDbScript(final String scriptPath) throws Exception {
try (InputStreamReader reader = new InputStreamReader(AbstractDaoTest.class.getResourceAsStream(scriptPath),
Charsets.UTF_8); Handle h = dbi.open()) {
RunScript.execute(h.getConnection(), reader);
}
}
}
Now you can overwrite the createRepository method in your test class.
public class ProductRepositoryTest() {
#Override
protected ProductRepository createRepository(Dbi dbi) { return new ProductRepository(dbi); }
#Test
public void testGetProductById() {
Product response = repository.getProductById(1);
assertThat(response).isEqualTo(someObject);
}
}
If you need a framework to mock objects you can use Mockito and if you need to mock static or void methods you can use PowerMock.
Hope this helps.
Add these annotations to your test classes.
#SpringBootTest
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DbUnitTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class YourTestClass {....
here's a working version of your example - hope this helps. I think you may have some conflicting configuration or dependencies in your own project. https://github.com/tndavidson/springbootjparepositorytest
I'm using Spring Data Jpa and facing an exception when I deployed the project
I have used findAll() method in service and serviceImpl class and I received an exception, this is my source:
Model:
#Entity
#Table(name="CITY")
public class City {
#Id
#Column(name="ID", nullable = false, unique = true)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name = "NAME")
private String name;
// get, set method
}
Repository class:
public interface CityJpaRepositoryCustom extends JpaRepository<City, Integer> {
}
Service class:
public interface CityService {
public List<City> findAll();
}
ServiceImpl class:
#Service
public class CityServiceImpl implements CityService {
#Resource
private CityJpaRepositoryCustom cityRepository;
#Override
#Transactional
public List<City> findAll() {
// TODO Auto-generated method stub
return cityRepository.findAll();
}
}
Controller class:
#Controller
public class CityController {
#Autowired
private CityService cityService;
#RequestMapping(value="/list", method=RequestMethod.GET)
public ModelAndView getListCity(HttpServletRequest request,
HttpServletResponse response,
ModelMap model) {
ModelAndView mav = new ModelAndView("manageCity");
List<City> cityList = cityService.findAll();
mav.addObject("cityList", cityList);
return mav;
}
Pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<artifactId>hibernate-entitymanager</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- SOLR -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-solr</artifactId>
<version>${spring-data-solr.verion}</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.11</version>
</dependency>
<!-- WEB -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<!-- Hibernate 4 dependencies -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
Log message:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cityJpaRepositoryCustom': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property delete found for type void!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
I have found some same topic but it's not get good result for me, How to fix this exception? thank you so much!
Upgrade your version of Spring Boot to the latest (1.5.1.RELEASE).
You're using an ancient version which will in turn be pulling in an old version of Spring Data which may have not supported delete.
If you want to use delete operation but don't want to update Spring Data JPA, you have to write your own delete implementation. Example:
public interface CityJpaRepositoryCustom extends JpaRepository<City, Integer> {
#Modifying
#Query("delete from City where name = :name")
void deleteByName(#Param("name") String name);
}
Related docs:
Using #Query annotation
#Modifying annotation
#Param annotation