Quarkus - How to test reactive flow for onFailure() - java

I have reactive quarkus resource where I am using Mongo Reactive Panache. I have a logic that if the emailId of person exist then code should throw BusinessException.
I am able to test the happy path but how do I trigger negative test. Here's my code and test class.
PersonResource.java
#POST
public Uni<Response> create(Person person){
return repository.persist(person). map(r ->
Response.ok(r).build())
.onFailure()
.recoverWithItem(f-> {
AStatus status = createErrorStatus(f.getMessage());
return Response.serverError().entity(status).build();
}) ;
}
PersonRepositoryTest.java
package com.eventu.resource;
import com.eventu.exception.BusinessException;
import com.eventu.repository.PersonRepository;
import com.eventu.vo.Address;
import com.eventu.vo.Person;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.mockito.InjectMock;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.helpers.test.UniAssertSubscriber;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import javax.ws.rs.core.Response;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
#QuarkusTest
class PersonResourceTest {
#InjectMock
PersonRepository repository;
#Inject
PersonResource resource;
Uni<Person> uniPerson;
Person person;
#BeforeEach
public void setUp(){
person = Person.builder().address(Address.builder()
.addressLine_1("1000 Test dr")
.addressLine_2("opposite crescent dr").suiteNumber("Ste. 200").city("testCity")
.countryCode("US").state("CA")
.build()).emailAddress("test#test.com").firstName("John").lastName("Barista")
.mobileNumber("70444444444")
.id(new ObjectId())
.build();
uniPerson = Uni.createFrom().item(person);
when(repository.persist(any(Person.class))).thenReturn(uniPerson);
}
// THIS TEST IS WORKING
#Test
#DisplayName("When a new person object is passed then person object with Id should be returned")
void create_success() {
final Uni<Response> p = resource.create(person);
Person respPerson = (Person) p.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted().getItem().getEntity();
Assertions.assertNotNull(p);
Assertions.assertEquals(person, respPerson);
}
/**
* THIS ONE FAILS WITH at line 63 with the message
* org.mockito.exceptions.base.MockitoException:
* Checked exception is invalid for this method!
* Invalid: com.eventu.exception.BusinessException
*/
#Test
#DisplayName("When a new person object is passed but email exists then AStatus object with error description")
void create_duplicate_emailId() {
when(repository.persist(any(Person.class))).thenThrow(BusinessException.class); /// **FAILS HERE. Tried replacing with RunTimeException.class as well**
final Uni<Response> p = resource.create(person);
Person respPerson = (Person) p.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted().getItem().getEntity();
//**SHOULD FAIL HERE as I EXPECT AStatus.java object**
Assertions.assertEquals(person, respPerson);
}
}

I found the solution after trying an error. The documentation on Mutiny/Quarkus leaves a lot to be desired.
This is what's needed -
#Test
#DisplayName("When a new person object is passed but email exists then AStatus object with error description")
void create_duplicate_emailId() {
when(repository.persist(any(Person.class)))
.thenReturn(Uni.createFrom().failure(new MongoException(11000, "Email does not exist")));
final Uni<Response> p = resource.create(person);
Person respPerson = (Person) p.subscribe().withSubscriber(UniAssertSubscriber.create()).assertCompleted().getItem().getEntity();
.....
}

Related

AssertEquals failing . List size is returning more than actual size

everyone. I'm new to Spring and unit testing and I'm trying to test my repository layer. My test is failing when using assertEquals when asserting the size of my products list. In the first test, it says actual is 3 when clearly it should be 2.Same goes for the second test, it's returning 2 when it should be 1 after deletion of product1.I don't know what I'm doing wrong.
Thank you in advance.
import com.eshop.shoppingcart.model.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class ProductRepositoryTest {
#Autowired
ProductRepository productRepository;
#Test
void getAllProducts(){
Product product1 = new Product();
product1.setId(1L);
product1.setName("The Stranger");
product1.setPrice(124.5);
product1.setPictureUrl("https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1590930002l/49552._SY475_.jpg");
Product product2 = new Product();
product2.setId(2L);
product2.setName("Meditations");
product2.setPrice(164.5);
product2.setPictureUrl("https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1590930002l/49552._SY475_.jpg");
productRepository.save(product1);
productRepository.save(product2);
List<Product> products = productRepository.findAll();
assertNotNull(products);
assertThat(products).isNotNull();
assertThat(products.size()).isEqualTo(2);
assertEquals(2, products.size());
}
#Test
void deleteProduct(){
Product product1 = new Product();
product1.setId(1L);
product1.setName("The Stranger");
product1.setPrice(124.5);
product1.setPictureUrl("https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1590930002l/49552._SY475_.jpg");
productRepository.save(product1);
Long id = product1.getId();
Product product2 = new Product();
product2.setId(2L);
product2.setName("Meditations");
product2.setPrice(164.5);
product2.setPictureUrl("https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1590930002l/49552._SY475_.jpg");
productRepository.save(product2);
productRepository.delete(product1);
List<Product> products = productRepository.findAll();
Optional<Product> retrievedProduct = productRepository.findById(id);
assertThat(retrievedProduct.isEmpty());
assertEquals(1, products.size());
}
}
You may add some code to clean the repository data.
It seems that you already have a product when the test starts.

Does findById() actually load data from a JPA repository?

I am a Hibernate beginner. I did a couple of simple tutorials and am trying to write a simple shop backend. Everything works as it should, but I am seeing strange things in my unit tests. When I save an entity, then retrieve it using findById(), it seems that I am simply getting the same object I called save() on, without even retrieving actual values from the database:
package com.bo.learnjava.shop1.repository;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "PRODUCTS")
public class Product {
#Id
#GeneratedValue
#Column(name="ID")
long id;
#Column(name="NAME")
String name = "";
#Column(name="PRICE_CENTS")
int priceCents = 0;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPriceCents() {
return priceCents;
}
public void setPriceCents(int priceCents) {
this.priceCents = priceCents;
}
public long getId() {
return id;
}
}
package com.bo.learnjava.shop1.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ProductRepository extends PagingAndSortingRepository<Product,Long> {
}
package com.bo.learnjava.shop1.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
#DataJpaTest
public class ProductRepositoryTest {
#Autowired
ProductRepository repo;
#Test
void testProductRepository() {
Product p=new Product();
p.setName("Milk");
p.setPriceCents(134);
repo.save(p);
// Modify the value to check that repo.findById() actually retrieves *saved* data
p.setPriceCents(9999);
Optional<Product> productFromRepo=repo.findById(p.getId());**
// I expect productFromRepo to contain the values I called save() with
// (price == 134). But productFromRepo.get() returns exactly the same Java object
// as p (with price == 9999), so no actual data was retrieved from the database - why?
assertTrue(productFromRepo.isPresent());
System.out.println("productFromRepo.priceCents="+productFromRepo.get().getPriceCents()); // Outputs 9999!
assertEquals(134,productFromRepo.get().getPriceCents()); // THIS FAILS!!!
}
}
Why does Hibernate behave like that, and how do I test that stuff I write to the database via Hibernate actually gets retrieved back from the database?
Additionaly to comment about first level cache.
If you are extending JpaRepository you can use
repo.saveAndFlush(p);
or
repo.save(p); repo.flush();
to immediately save data in DB.
After it - repo.findById(p.getId()); will return updated data.

Why does a #Query annotated method require a body?

I'm new to Spring.
I wrote a class:
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
public class Kontroller
{
#Query( value="select * from teszt"
, nativeQuery = true
)
List<Teszt> osszesSor(); // <-- error
//.....
#RequestMapping("/szam")
public #ResponseBody String szamossag()
{
List<Teszt> sokasag = osszesSor();
return("számosság="+sokasag.size());
}
}
And it says (it = the IDE (STS), and at runtime when I call osszesSor()):
This method requires a body instead of a semicolon.
The Teszt class is:
import java.io.Serializable;
import javax.persistence.*;
#Entity
#IdClass(Teszt.class)
public class Teszt implements Serializable {
#Id
private String kulcs;
#Id
private String ertek;
//getters, setters
}
It's a very simple table (that's why it becomes very complex in spring, because it has not a one-column key):
create table TESZT
(
KULCS VARCHAR2(2000) not null,
ERTEK VARCHAR2(2000) not null,
constraint TESZT_UN
unique (KULCS, ERTEK)
)
Now I know.
I had to create a repo interface:
public interface TesztRepo extends Repository<Teszt, Teszt>
{
#Query( value="select * from teszt"
, nativeQuery = true
)
List<Teszt> sokasag();
//...
}
and autowire it in the controller.
#Autowired
TesztRepo dao;
//...
List<Teszt> sokasag = dao.sokasag();
Not that complex.

REST: How do I build a request path with a parameter as first token?

I am new to RESTful services. I usually develop Java EE applications and SOAP services in a JBoss / Wildfly environment. I am currently trying to find my way into RESTful services to broaden my knowledge. Since I am feeling familiar with JBoss / Wildfly I decided to go with RESTEasy.
I decided to create a RESTful service for an example pet shop chain. As a chain the pet shop has multiple stores which are identfied by a shop id (e.g. shop1, shop2, etc.). I have created multiple REST services to segment services based on technical functionality (e.g. article services => article.war, order service => orde.war, etc.
I want to create human readable URLs, like:
GET:
http://mypetshop.example/rest/{shopId}/article/{articleId}
POST with JSON formatted order content:
http://mypetshop.example/rest/{shopId}/order/create
So far I have only managed to create URLs like:
GET:
http://mypetshop.example/rest/article/{shopId}/{articleId}
POST with JSON formatted order content:
http://mypetshop.example/rest/order/create/{shopId}
Is my wanted REST path possible or do I have to keep up with my current solution?
Best regards,
CB
Here is an example code for article services:
ArticleRestApplication.java:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath(ArticleRestApplication.ROOT_PATH)
public class OrderRestApplication extends Application {
public static final String ROOT_PATH = "/article";
}
ArticleService.java
public interface ArticleService{
Article getArticle(String shopId, Integer articleId);
}
ArticleServiceImpl.java:
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.google.gson.Gson;
#Path("/")
#Consumes(MediaType.APPLICATION_JSON + ";charset=UTF-8")
#Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
public class ArticleServiceImpl implements ArticleService {
public ArticleServiceImpl() {
super();
}
#GET
#Path("/{shopId}/{articleId}")
public Article getArtikel(
#PathParam("shopId") String shopId,
#PathParam("articleId") Integer articleId) {
System.out.println(String.format("Shop ID: \"%s\"", shopId));
System.out.println(String.format("Article ID: \"%s\"", articleId));
return gson.toJson(new Article(articleId));
}
}
Article.java:
import java.io.Serializable;
import java.math.BigDecimal;
import javax.xml.bind.annotation.XmlRootElement;
#SuppressWarnings("serial")
#XmlRootElement(name = "article")
public class Article implements Serializable {
private String shopId;
private int articleId;
private String name = "Super pet food";
private BigDecimal price = new BigDecimal("1.00");
private int unitsInStock = 1000;
public Article(String shopId, int articleId) {
super();
this.shopId = shopId;
this.articleId = articleId;
}
}
Yes you can do
like below
rest/orders/1/completed
Here rest in rest servlet path , orders for class, then using #Path("{orderId}/completed")
#Path("orders")
public class OrderService {
#GET
#Path("{orderId}/completed")
public String getOrders(#PathParam("orderId") String orderId) {
return "orderId: " + orderId;
}
#GET
#Path("summary")
public String getOrdersSummary() {
return "orders summary";
}
}
Live demo at http://jerseyexample-ravikant.rhcloud.com/rest/orders/1/completed

Implement Subtotal feature of Excel

Excel provides the Subtotal option from the Menu Data -> Outline -> Subtotal. It creates automatically the sub-sums and the possibility to fold the data. The image below demonstrates how the action transforms the sheet.
And this is exactly what I need to do via POI. I know how to set a subtotal function into a cell so I could calculate the intermediate sums by myself. But how do I enable this folding on the left border?
I realised there is the groupRow() method but those nested groups doesn't work like they should. If I use the following code I only get two groups. One large (1-7) and (1-3). The group (5-7) is missing and changing the order of the calls has no effect.
sheet.groupRow(1, 7);
sheet.groupRow(1, 3);
sheet.groupRow(5, 7);
I use a quite old verion of POI but this is how I did it:
I also needed multiple nested groups so I had a model for the rows where the indent level was stored as well (it was a tree so the indent was implicit). I traversed the model with a visitor to get the group start and end row numbers. Then called HSSFSheet.groupRow subsequently for each group. If I remember correctly, the order of the group calls is important.
I think this is exactly what you are looking for:
http://www.mysamplecode.com/2011/10/apache-poi-excel-row-group-collapse.html
if you use subtotal(9,<range>) instead of sum(<range>), you can perform nested groups as subtotal ignores cell with subtotal in its range
With following library you can to calculate the subtotals that you want
<dependency>
<groupId>com.github.bld-commons.excel</groupId>
<artifactId>generator-excel</artifactId>
<version>3.1.1</version>
</dependency>
This library is wrapper of apache poi.
Below the source code:
You create a class that represents the row of the table.
package bld.generator.report.junit.entity;
import org.apache.poi.ss.usermodel.DataConsolidateFunction;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import bld.generator.report.excel.RowSheet;
import bld.generator.report.excel.annotation.ExcelCellLayout;
import bld.generator.report.excel.annotation.ExcelColumn;
import bld.generator.report.excel.annotation.ExcelFont;
import bld.generator.report.excel.annotation.ExcelSubtotal;
import bld.generator.report.excel.annotation.ExcelSubtotals;
#ExcelSubtotals(labelTotalGroup = "Total",endLabel = "total")
public class SalaryRow implements RowSheet {
#ExcelColumn(columnName = "Name", indexColumn = 0)
#ExcelCellLayout
private String name;
#ExcelColumn(columnName = "Amount", indexColumn = 1)
#ExcelCellLayout(horizontalAlignment = HorizontalAlignment.RIGHT)
#ExcelSubtotal(dataConsolidateFunction = DataConsolidateFunction.SUM,excelCellLayout = #ExcelCellLayout(horizontalAlignment = HorizontalAlignment.RIGHT,font=#ExcelFont(bold = true)))
private Double amount;
public SalaryRow() {
super();
}
public SalaryRow(String name, Double amount) {
super();
this.name = name;
this.amount = amount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
}
You create a class that represents the sheet.
package bld.generator.report.junit.entity;
import javax.validation.constraints.Size;
import bld.generator.report.excel.SheetData;
import bld.generator.report.excel.annotation.ExcelHeaderLayout;
import bld.generator.report.excel.annotation.ExcelMarginSheet;
import bld.generator.report.excel.annotation.ExcelSheetLayout;
#ExcelSheetLayout
#ExcelHeaderLayout
#ExcelMarginSheet(bottom = 1.5,left = 1.5,right = 1.5,top = 1.5)
public class SalarySheet extends SheetData<SalaryRow> {
public SalarySheet(#Size(max = 31) String sheetName) {
super(sheetName);
}
}
Class test
package bld.generator.report.junit;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import bld.generator.report.excel.BaseSheet;
import bld.generator.report.excel.GenerateExcel;
import bld.generator.report.excel.data.ReportExcel;
import bld.generator.report.junit.entity.SalaryRow;
import bld.generator.report.junit.entity.SalarySheet;
import bld.generator.report.utils.ExcelUtils;
#RunWith(SpringRunner.class)
#SpringBootTest
#ConfigurationProperties
#ComponentScan(basePackages = {"bld.generator","bld.read"})
#EnableTransactionManagement
public class SalaryTest {
private static final String PATH_FILE = "/mnt/report/";
#Autowired
private GenerateExcel generateExcel;
/**
* Sets the up.
*
* #throws Exception the exception
*/
#Before
public void setUp() throws Exception {
}
#Test
public void testSalary() throws Exception {
List<BaseSheet> listBaseSheet = new ArrayList<>();
SalarySheet salarySheet=new SalarySheet("salary");
salarySheet.getListRowSheet().add(new SalaryRow("a",2.0));
salarySheet.getListRowSheet().add(new SalaryRow("a",2.0));
salarySheet.getListRowSheet().add(new SalaryRow("a",2.0));
salarySheet.getListRowSheet().add(new SalaryRow("a",2.0));
salarySheet.getListRowSheet().add(new SalaryRow("c",1.0));
salarySheet.getListRowSheet().add(new SalaryRow("c",1.0));
salarySheet.getListRowSheet().add(new SalaryRow("c",1.0));
salarySheet.getListRowSheet().add(new SalaryRow("c",1.0));
listBaseSheet.add(salarySheet);
ReportExcel report=new ReportExcel("test", listBaseSheet);
byte[] byteReport = this.generateExcel.createFileXlsx(report);
ExcelUtils.writeToFile(PATH_FILE,report.getTitle(), ".xlsx", byteReport);
}
}
Below the link of the project on github:
https://github.com/bld-commons/dev-excel
Below the result.
enter image description here

Categories

Resources