I wrote a functional test to check adding items to a shopping cart.For a user to be able to add items to cart,he needs to login.So,I created a method to login the user and another method to add the item.Before and after the addtocart method in test,I am checking the size of content of cart.The addtocart functionality works without any problem when I run the app in dev mode(I can check the db too-which is postgres and not an in memory db).The addtocart fails in test.
the controller method which adds item to cart
public static void addItemToCart(Long productId,Long cartId,String quantity) {
Product product = Product.findById(productId);
ShopCart cart = ShopCart.findById(cartId);
int qty = Integer.parseInt(quantity);
CartItem cartItem = new CartItem(product,qty);
cart.addItem(cartItem);
cart.save();
System.out.println("Controller::addItemToCart()::cart id="+cart.id+" has="+cart.cartItems.size()+" items);
}
my test method is
#Test
public void testUserCanAddItemsToCart() {
Fixtures.loadModels("data.yml");
User user = User.find("byEmail","user#shop.com").first();
loginAsCustomer("user#shop.com","userpass");
ShopCart usercart = new ShopCart(user);
usercart.save();
System.out.println("BEFORE ADD::usercart="+usercart.id+" has :"+usercart.cartItems.size()+" items");
assertTrue(usercart.cartItems.size()==0);
addItemsToCart(usercart);
System.out.println("AFTER ADD::usercart="+usercart.id+" has :"+usercart.cartItems.size()+" items");
assertFalse(usercart.cartItems.size()==0);//why does this fail?
}
private Response addItemsToCart(ShopCart cart) {
Product pdt = Product.find("byIsbn","654-0451160522").first();
assertNotNull(pdt);
System.out.println("addItemsToCart():BEFORE ADD cart="+cart.id+" has="+cart.cartItems.size());
Map<String,String> addtocartParams = new HashMap<String,String>();
addtocartParams.put("cartId", cart.id.toString());
addtocartParams.put("quantity", "2");
String addtocarturl = "/items/addtocart/"+pdt.id.toString();
Response response = POST(addtocarturl,addtocartParams);
System.out.println("addItemsToCart():AFTER ADD cart="+cart.id+" has="+cart.cartItems.size());
return response;
}
The console output I get is
BEFORE ADD::usercart=48 has :0 items
addItemsToCart():BEFORE ADD cart=48 has=0
Controller::addItemToCart()::cart id=48 has=1 items
addItemsToCart():AFTER ADD cart=48 has=0
AFTER ADD::usercart=48 has :0 items
Here, in the controller method, the cart instance (of id=48) has 1 item after it is saved to db.But in the test method ,the cart instance of same id has 0 content.
I commented out the assertFalse method and retrieved the cart from db using the cartId.Even then the cart of same id has 0 content.I can't understand why this is happening..can anyone shed some light?
//test method body ..modified
ShopCart cart = ShopCart.findById(usercart.id);
System.out.println("AFTER ADD::cart="+cart.id+" has :"+cart.cartItems.size()+" items");
assertFalse(cart.cartItems.size()==0);//why does this fail?
It fails because the cart instance used by your test method and the cart instance used by the addItemToCart method are different. Each transaction has its own instance of the entity. And JPA doesn't automagically refresh an entity when some other transaction updates the row mapped by this entity.
You should reload the cart from the database after addItemsToCart has been called to check if something has been added to the cart in database.
I am a slave to object-oriented thinking, so what I'm wondering is, have you thought about making addItemsToCart() a method of your ShopCart class? I'm envisioning something like:
...
ShopCart usercart = new ShopCart(user);
usercart.addItemsToCart(pdt);
usercart.save();
String addtocarturl = "/items/addtocart/"+pdt.id.toString();
Response response = POST(addtocarturl,addtocartParams);
return response;
It's just easier for me to think about making (or retrieving) a ShopCart object, modifying it, and putting it in the database. That's how I would avoid this.
I had the same issue and adding JPA.em().clear() in my test before I get the model from the database solved this issue for me.
Related
I have the following service method:
public CommandDTO update(UUID uuid, EmployeeRequest request) {
final List<Product> productList = productRepository
.findAllByUuidOrderByOrderNumber(uuid);
final List<UUID> uuidList = request.getUuidList();
for (int i = 0; i < uuidList.size(); i++) {
Product product = productList.get(i);
product.setOrderNumber(i + 1);
// how can I get the product value in this line?
}
return CommandDTO.builder().uuid(uuid).build();
}
Normally, I use ArgumentCaptor in order to get values passing to repository or service. Howevcer, in this example, the product value that I want to get after product.setOrderNumber(i + 1); is not passing service or repository. For this reason I cannot get it. I am not sure if it would be possible using #Spy or doAnswer, but as far as I know, these methods cannot be used as the value is not passing to service or repo. Any idea?
You can mock you repository to return list of mock's:
public test() {
Product product1 = mock(Product.class);
Product product2 = mock(Product.class);
List list = Arraylist();
list.add(product1);
list.add(product2);
when(productRepositoryMock.findAllByUuidOrderByOrderNumber(uuid))
.thenReturn(list)
when(product1).setOrderNumber(captor.capture)
In this case you can use ArgumentCaptor here, but I belive it's redundant in this case, and simple verify call will be enough
service.update(.....);
verify.(product1).setOrderNumber(value);
The product variable comes from productList, which in turn comes from the repository.findAllByUuidOrderByOrderNumber() method. So if you mocked your repository, you could write something like this:
Product givenProduct = new Product();
UUID givenUUID = UUID.randomUUID();
// Create an EmployeeRequest with a `uuidList`
EmployeeRequest givenRequest = new EmployeeRequest(/* TODO */);
// Use Mockito to return a `productList` you choose
// This uses a static import of `Mockito.when()`
when(productRepository.findAllByUuidOrderByOrderNumber(givenUUID)).thenReturn(List.of(givenProduct));
service.updateRequest(givenUUID, givenRequest);
Theoretically you could even return a mocked product in the test logic above, but I suggest using a real Product object.
And now whatever happens to that product, can be asserted within your test.
For example:
// `givenProduct` is element 0, and thus will get an orderNumber of 1
assertThat(givenProduct.getOrderNumber()).isEqualTo(1);
I am building an online hotel reservation system. for that I am using a reservation cart in which I keep track of rooms added by the user. When I am adding some rooms in a browser login in with user1 , now when I log in with some other browser with user2 and add some other rooms in the cart. Now when I try to add another room in cart of user1 , the existing cart of user1 gets replaced by the cart values of user2. I am maintaining different sessions for each user but the cart attribute is getting same for all the sessions . I am currently working on local host. please help
this is how I am adding values to rbList and setting the session attribute.
if(request.getParameter("button").equals("addRoom"))
{ // System.out.println("******************inside addRoom*****************");
if(session!=null)
{
availableRooms = (ArrayList<HotelBean>)session.getAttribute("availableRooms");
int addPos = Integer.parseInt(request.getParameter("roomPosition"));
rb = availableRooms.get(addPos);
String roomID = rb.getRoomId();
HoteDAO hd = new HoteDAO();
boolean available = hd.isAvailable(roomID);
if(available){
if((ArrayList<HotelBean>)session.getAttribute("ReservationCart") == null){
rbList = new ArrayList<HotelBean>();
}
if(rbList == null){
rbList = new ArrayList<HotelBean>();
}
rbList.add(rb);
for(HotelBean room : rbList){
System.out.println(room.getRoomId());
}
session.setAttribute("ReservationCart",rbList);
RequestDispatcher rd=request.getRequestDispatcher("/AvailableRooms.jsp");
rd.forward(request,response);
}
else
{
System.out.println("Sorry the room is not available now");
RequestDispatcher rd=request.getRequestDispatcher("/AvailableRooms.jsp");
rd.forward(request,response);
}
}
}
Your last comment is the answer to your problem. You instantiate the rbList in a class that has only one instance across your web application. Hence you only have only one rbList accross all customers and that causes your problem.
Although I do not have a very clear picture of how your rbList work, I am confident that each customer should have their own unique rbList or at least their own cart.
Also, declaring the customer cart in a controller seems like a poor design choice.
The way that I would go about your problem would be create a separate CartModel class that contains everything cart related.
Create a one to one or one to many relationship from your CustomerModel to your CartModel.
Preferably save the CartModel(s) to your database or at session if your prefer to go with this design.
I hope I helped.
I have created two services which uses Spring Cache. In the first method I am putting all records which are made while app is starting:
#CachePut(value = "items")
public Item save(Item item){
System.out.println("----------------- z cachea udated" + item);
return itemRepository.saveAndFlush(item);
}
So when app is running in first steps I populate my cache. Then I am getting all records:
#Cacheable(value = "items")
public Page<Item> getItems(Pageable pageRequest){
System.out.println("----------------- z cachea PAGEABLE");
Page<Item> all = itemRepository.findAll(pageRequest);
System.out.println("----------------- all" + all.getContent());
return all;
}
Then I update only one record using save() method and again getting all records. And in that moment my cache hasn't been changed at all. Inside the cache there all records which were in the first call of method. How to change inside the cache only that record which has been affected by the update?
Try changing #Cacheable(value = "items") to #Cacheable(value="items", key="all")
I'm using the Spring Data Neo4j 4. It seems the "PersistenceContext" of Neo4j cache the values of the "Set" value.
The Entity
#NodeEntity
public class ServiceStatus implements java.io.Serializable {
#GraphId Long id;
private Set<String> owners = new HashSet<String>();
}
First, I put a value "ROLE_ADMIN" in the owners and save it.
Then I edit the value to "ROLE_SYSTEM_OWNER" and called save() again.
In the Neo4j query browser, it only show the "ROLE_SYSTEM_OWNER", which is all correct for now.
However, when I called the findAll(), the owners has two values ["ROLE_ADMIN","ROLE_SYSTEM_OWNER"]
It will work fine when I restart my web server.
[The way to change value]
#Test
public void testSaveServiceStatus() throws OSPException {
//1. save
ServiceStatus serviceStatus = new ServiceStatus();
serviceStatus.setServiceName("My Name");
Set<String> owners = new HashSet<String>();
owners.add("ROLE_SITE_ADMIN");
serviceStatus.setOwners(owners);
serviceStatusRepository.save(serviceStatus);
System.out.println(serviceStatus.getId()); //262
}
#Test
public void testEditServiceStatus() throws OSPException{
//1. to find all , it seems cache the set value
serviceStatusRepository.findAll();
//2. simulate the web process behavior
ServiceStatus serviceStatus = new ServiceStatus();
serviceStatus.setId(new Long(262));
serviceStatus.setServiceName("My Name");
Set<String> owners = new HashSet<String>();
//change the owner to Requestor
owners.add("Requestor");
serviceStatus.setOwners(owners);
//3. save the "changed" value
// In the cypher query browser, it show "Requestor" only
serviceStatusRepository.save(serviceStatus);
//4. retrieve it again
serviceStatus = serviceStatusRepository.findOne(new Long(262));
System.out.println(serviceStatus); //ServiceStatus[id=262,serviceName=My Name,owners=[Requestor5, Requestor4]]
}
Your test appears to be working with detached objects in a way. Step one, findAll() loads these entities into the session, but then step 2 instead of using the loaded entity, creates a new one which is subsequently saved. The "attached" entity still refers to the earlier version of the entity.
The OGM does not handle this currently.
You're best off modifying the entity loaded in findAll or just a findOne(id), modify, save (instead of recreating one by setting the id). That will ensure everything is consistent.
i am work on an online credit topup application. now i want to fetch the denomination available to a particular type of voucher which is select by.this is the scenario
a user selects a type of voucher(tigo,mtn,vodafon,airtel etc) from a combo box.after the selection the voucher denomination combobox should be populated with all denominations available. for now am out putting with json .av tried with this code but it returns this:
#RequestMapping(value = "/rest/getdenominations")
public #ResponseBody
String getDenominationByType(ModelMap model) throws JSONException {
Long vouchertypeId = 1L;
JSONArray typeDenominationsArray = new JSONArray();
for (Voucher voucher : voucherController.getTypDenominationAvailable(vouchertypeId)) {
JSONObject voucherJSON = new JSONObject();
voucherJSON.put(" ", voucher.getDenomination());
typeDenominationsArray.put(voucherJSON);
}
return typeDenominationsArray.toString();
}
but it returns this
[{" ":10},{" ":2},{" ":1},{" ":10},{" ":2}]
what i want is that the 2 and 10 should be displayed just once.
need help to this..tanx in advance
I don't think that the JSONArray object has a contains(JSONObject obj) function but you can make one yourself (See this link for an example). With this, inside of the for loop after you create each JSONObject check if your JSONArray contains the object. If not, add it, otherwise dont add it. This is not the most elegant solution but it should work.