Predefined mock response in Spock - testing

I'm new to Spock and this question refers to the example on page 178 of Java Testing with Spock. The class under test is the Basket class for a shopping application, and the method of this class being tested is canShipCompletely()
public class Basket {
private WarehouseIneventory warehouseInventory;
private ShippingCalculator shippingCalculator;
protected Map<Product, Integer> contents = new HashMap<>();
...
public void addProduct(Product product) {
addProduct(product, 1);
}
public void addProduct(Product product, int times) {
if (contents.containsKey(product)) {
int existing = contents.get(product);
contents.put(product, existing + times);
} else {
contents.put(product, times);
}
}
public Boolean canshipCompletely() {
if(warehouseInventory.isEmpty()) return false;
try {
for (Entry<Product, Integer> entry : contents.entrySet())
boolean ok = warehouseInventory.isProductAvailable(
entry.getKey().getName(),
entry.getValue()
);
if (!ok) {
return false;
}
}
return true;
} catch (Exception e) {
return false;
}
...
}
This method canShipCompletely() loops over the items in the basket (in Map contents) and for each item, it makes a call to warehouseInventory.isProductAvailable(product, count) to see if there is sufficient stock in the warehouse to fill the order. The Warehouse class is a collaborator of the Basket class that is mocked in the following test
def "Warehouse is queried for each product"() {
given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18)
Product camera = new Product(name:"panasonic",price:350,weight:2)
Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory)
basket.setWarehouseInventory(inventory)
when: "user checks out two products"
basket.addProduct tv
basket.addProduct camera
boolean readyToShip = basket.canShipCompletely()
then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _) >> true
0 * inventory.preload(_ , _)
}
The then: block verifies the boolean readyToShip is true, and that inventory.isProducAvailable() was called twice and inventory.preload() was not called at all. The next to last line is both checking behavior of the mock and telling it to return true for calls to isProductAvailable(). What I don't understand is the test will fail if I move the mock predefined response to the and: block as follows
def "Warehouse is queried for each product"() {
given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18)
Product camera = new Product(name:"panasonic",price:350,weight:2)
Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory)
// ******** Move mock predefined response here **********
inventory.isProductAvailable( _ , _ ) >> true
basket.setWarehouseInventory(inventory)
when: "user checks out two products"
basket.addProduct tv
basket.addProduct camera
boolean readyToShip = basket.canShipCompletely()
then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _)
0 * inventory.preload(_ , _)
}
The failure I get is too few calls to isProductAvailable():
Too few invocations for:
2 * inventory.isProductAvailable( _ , _) (1 invocation)
Unmatched invocations (ordered by similarity):
1 * inventory.isEmpty()
I don't understand why the predefined behavior for the mock can't be moved to the and: block.

Please refer to the documentation
When mocking and stubbing the same method call, they have to happen in the same interaction. In particular, the following Mockito-style splitting of stubbing and mocking into two separate statements will not work:
setup:
subscriber.receive("message1") >> "ok"
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
As explained in Where to Declare Interactions, the receive call will first get matched against the interaction in the then: block. Since that interaction doesn’t specify a response, the default value for the method’s return type (null in this case) will be returned. (This is just another facet of Spock’s lenient approach to mocking.). Hence, the interaction in the setup: block will never get a chance to match.

Related

JAVA How to count unique objects in an ArrayList, each objects has a string = name , field and an int = price, field

So I have an arraylist basket that stores items, each item is made out of the name of the item and the price of the item fields.
As you can see there's two sugar with same price.
I want my code to print every single item with the amount of time its repeated.
What I want it to do is this:
Count the duplicate values
2 x Sugar for 100
1 x Cake for 75
1 x Salt for 30
1 x Fanta for 50
My Item class toString's method is
public String toString(){
return name + " for " + price;
}
basket = new ArrayList<Item>();
basket.add(new Item("Sugar", 100));
basket.add(new Item("Sugar", 100));
basket.add(new Item("Cake", 75));
basket.add(new Item("Salt", 30));
basket.add(new Item("Fanta", 50));
HashSet<Item> set = new Hashset(basket)
for (Item item : set ){
System.out.println(Collections.frequency(basket, item) + " x" + item);
}
But what it does is...
1 x Sugar for 100
1 x Sugar for 100
1 x Cake for 75
1 x Salt for 30
1 x Fanta for 50
so i'm thinking that its comparing the toStrings but the repeated ones are not equalling to true.
please help.
This is my first ever post and don't really know the exact rules of posting
It would be better to use a Map in this scenario. First, make sure you are implementing the equals(Object) and hashCode() methods in your Item object, then putting each unique Item into the map with a value of an AtomicInteger with base value 1. Before putting in the object in the map you should check to make sure the item is not already in the map, if it already exists in the map, get the AtomicInteger and increment it.
Something like this..
Map<Item, AtomicInteger> quantitiesByItem = new HashMap<>();
for (Item item : listOfItems) {
if (!quantitiesByItem.contains(item)) {
quantitiesByItem.put(item, new AtomicInteger(1));
} else {
quantitiesByItem.get(item).incrementAndGet();
}
}
A HashSet actually prevents duplicates so would only be useful in a scenario where you are trying to strip duplicate records.
EDIT: You could also use Collections.frequency but you need to implement equals(Object) and hashCode() in your Item object otherwise the objects are determined to be different because they aren't the exact same object. You will also need to stop adding all your values from your list to a set and instead just pass your List object to Collections.frequency method.
This is what my code was missing in my Item class;
You need to override the equals and hashcode methods that every objects have by default. This will allow you to compare two objects by their state.
object A with to private fields e.g. int field and a String field
with object B. If both fields are the same there return value will be true
#Override
public boolean equals(Object obj){
//check for null
if(this == null){
return false;
}
//make sure obj is a item
if(getClass() != obj.getClass()){
return false;
}
//cast obj as an item
final Item passsedItem = (Item) obj;
//check fields
if(!Objects.equals(this.name, passsedItem.name)){
return false;
}
if(!Objects.equals(this.price, passsedItem.price)){
return false;
}
return true;
}
/**
* Override hashcode method to be able to compare item state to see if they are the same
* #return
*/
#Override
public int hashCode() {
int hash = 7;
hash = 31 * hash + Objects.hashCode(this.price);
return hash;
}

Example Program to insert a row using BAPI with JCO3

I am trying to "insert" (or) "add a row" to Purchase Requisition using standard BAPI (PurchaseRequisition.CreateFromData).
I am using JCo3. The example in JCo3 indicates that we should use table.appendRow() OR table.insertRow() methods. I am trying with table.appendRow() & table.appendRows(1). When i try to insert a row, i dont get any error and the row is not inserted.
Below is the program i am trying to execute.
/** Below are the inputs required for this program to run /
/ Step 1 **/
String BAPI_NAME = "BAPI_REQUISITION_CREATE";
/** Step 2 **/
String query_input_column1 = "DOCUMENTY_TYPE";
String query_input_column1_value = "NB";
String query_input_column2 = "PREQ_NAME";
String query_input_column2_value = "Name";
String query_input_column3 = "ACCTASSCAT";
String query_input_column3_value = "U";
String query_input_column4 = "DELIV_DATE";
String query_input_column4_value = "20131101";
String query_input_column5 = "MATERIAL";
String query_input_column5_value = "DELL-RQ2013";
String query_input_column6 = "QUANITY";
int query_input_column6_value = 10100;
/** Step 3 **/
String targetTableUnderBAPI = "REQUISITION_ITEMS";
/** Step 4 **/
/** For the confirmation read the value from export parameter after insertion execution **/
String result_column1 = "NUMBER";
JCoDestination destination = null;
try {
destination = JCoDestinationManager.getDestination(DestinationManager.DESTINATION_NAME1);
JCoRepository repository = destination.getRepository();
JCoContext.begin(destination);
JCoFunction function = repository.getFunction(BAPI_NAME);
if(function == null)
throw new RuntimeException(BAPI_NAME + " not found in SAP.");
System.out.println("BAPI Name from function object: " + function.getName());
//function.getImportParameterList().setValue(query_input_column1, query_input_column1_value);
JCoTable table = function.getTableParameterList().getTable(targetTableUnderBAPI); //it is taken from the response value of metadata
//System.out.println("No of Columns: "+ table.getNumColumns());
System.out.println("Trying to execute append row");
table.appendRow();
table.setValue(query_input_column1,query_input_column1_value);
table.setValue(query_input_column2,query_input_column2_value);
table.setValue(query_input_column3,query_input_column3_value);
//table.setValue(query_input_column4,new java.util.Date(query_input_column4_value));
//skipped Other columns related code
try{
function.execute(destination);
}
catch(AbapException e){
System.out.println(e.toString());
return;
}
System.out.println("Let us check the result from export parameter");
String exportParamStructure = (String)function.getExportParameterList().getValue(result_column1); //getStructure(result_column1); // getValue(result_column1);
System.out.println("Resulting PR#: "+exportParamStructure);
} catch (JCoException e) {
e.printStackTrace();
}
finally
{
try {
JCoContext.end(destination);
} catch (JCoException e) {
e.printStackTrace();
}
}
I did not understand how to read the response and am trying to fetch it from exportParameters!!
Can anybody share a piece of code to insert and
getting confirmation response (do we get the PREQ_NO in response?)
I am adding date field value as "20131101", but not sure if the format and approach is right?
when i try to add Quantity column value, i get an error message complaining this column is not part of BAPIEBANC. But the column is visible in BAPIEBANC type.
any configuration on SAP side to be checked?
should i activate any fields in JCo side? if so, how
Please note that my knowledge on SAP is very limited.
Waiting for an expert's response.
Thanks.
First, you should take a look at SAP JCo documentation, e.g.
http://help.sap.com/saphelp_nw04/helpdata/en/6f/1bd5c6a85b11d6b28500508b5d5211/content.htm
Regarding your code:
Adding (one) row to the table looks right on first sight.
Your code says QUANITY instead of QUANTITY.
You should add date values as java.util.Date; if creating a Date from a String format, you should use java.text.DateFormat.parse(). See http://docs.oracle.com/javase/6/docs/api/java/util/Date.html (this is however Java specific and has nothing to do with JCo).
If changing anything in SAP, never forget to call BAPI_TRANSACTION_COMMIT in the end to finish the logical unit of work (aka transaction) or nothing will actually be changed.
If you don't like to fiddle with the more or less complicated and verbose JCo API, try using Hibersap which gives you a much nicer programming model when calling functions in SAP ERP: http://hibersap.org.
However, you will still need a basic understanding on how SAP function modules work technically (such as parameter types or data types) as well as on the domain specific model which lies behind them (in your case, creating a requisition). I.e. you may need to communicate with your SAP experts.
Here I added 2 types of insertion :
insertval() function for user defined module resides in sap with the help of abap programmer
Its an standard module for insert a ticket using jco to SOLMAN system. First you have to analyse import, export, table & structure parameters, and according to that you have to pass values and retrieve response. In second function it will return ticket n° after successfull insertion of ticket in solman.
I hope this sample code will help you, it worked for me.
public class jco
{
static String DESTINATION_NAME1 = "ABAP_AS_WITHOUT_POOL";
static String DESTINATION_NAME2 = "ABAP_AS_WITH_POOL";
static
{
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "192.1.1.1");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "01");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "500");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "uname");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "pwd");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "en");
createDestinationDataFile(DESTINATION_NAME1, connectProperties);
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");
createDestinationDataFile(DESTINATION_NAME2, connectProperties);
System.err.println("hai");
}
static void createDestinationDataFile(String destinationName, Properties connectProperties)
{
File destCfg = new File(destinationName+".jcoDestination");
try
{
try (FileOutputStream fos = new FileOutputStream(destCfg, false)) {
connectProperties.store(fos, "for tests only !");
}
}
catch (IOException e)
{
throw new RuntimeException("Unable to create the destination files", e);
}
}
public void insertval() throws JCoException
{
JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME1);
JCoFunction jf=destination.getRepository().getFunction("ZUSER_DET");
jf.getImportParameterList().setValue("FIRST_NAME","member");
jf.getImportParameterList().setValue("LAST_NAME","c");
jf.getImportParameterList().setValue("USER_NO","1000");
jf.execute(destination);
System.out.println(jf);
}
public void insertticket() throws JCoException
{
JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME2);
System.out.println("test"+"\n");
JCoFunction jf=destination.getRepository().getFunction("BAPI_NOTIFICATION_CREATE");
JCoTable jt1=jf.getTableParameterList().getTable("APPX_HEADERS");
JCoTable jt2=jf.getTableParameterList().getTable("APPX_LINES");
JCoTable jt3=jf.getTableParameterList().getTable("APPX_LINES_BIN");
JCoTable jt4=jf.getTableParameterList().getTable("NOTIF_NOTES");
JCoTable jt5=jf.getTableParameterList().getTable("NOTIF_PARTNERS");
JCoTable jt6=jf.getTableParameterList().getTable("NOTIF_SAP_DATA");
JCoTable jt7=jf.getTableParameterList().getTable("NOTIF_TEXT_HEADERS");
JCoTable jt8=jf.getTableParameterList().getTable("NOTIF_TEXT_LINES");
JCoStructure jfn1=jf.getImportParameterList().getStructure("NOTIF_EXT");
JCoStructure jfn2=jf.getImportParameterList().getStructure("NOTIF_CRM");
JCoStructure jfn3=jf.getImportParameterList().getStructure("IBASE_DATA");
jfn1.setValue("NUMB","1234");
jfn1.setValue("REFNUM","123");
jfn1.setValue("TYPE_NOTIF","SLFN");
jfn1.setValue("SUBJECT","tl");
jfn1.setValue("PRIORITY","2");
jfn1.setValue("LANGUAGE","EN");
jfn1.setValue("CATEGORY","Z01");
jfn2.setValue("CODE","0011");
jfn2.setValue("CODEGROUP","0011");
jfn2.setValue("CATEGORY","Z01");
jfn3.setValue("INSTANCE","489");
jfn3.setValue("IBASE","500");
jt1.appendRow();
jt1.setValue("DESCR","practise");
jt2.appendRow();
jt2.setValue("LINE","CVXCVXCV");
jt3.appendRow();
jt3.setValue("LINE","second text line");
jt4.appendRow();
jt4.setValue("TYPE_NOTE","my");
jt4.setValue("IDENT","hoe twwrtgw");
jt4.setValue("DESCRIPTION","its description ");
jt5.appendRow();
jt5.setValue("PARNR","new ");
jt5.setValue("TYPE_PAR","FN");
jt5.setValue("FUNC_PAR","EN");
jt5.setValue("PAR_ACTIVE","1");
jt6.appendRow();
jt6.setValue("INSTN","0020214076");
jt6.setValue("COMP","FI-AA");
jt6.setValue("SYSTYPE","P");
jt6.setValue("SYSID","PRD");
jt6.setValue("MANDT","900");
jt8.appendRow();
jt8.setValue("TXT_NUM","1");
jt8.setValue("TDFORMAT",">X");
jt8.setValue("TDLINE","/(performing all test)");
jf.execute(destination);
String jfex=jf.getExportParameterList().getString("REFNUM");
System.out.println("hi "+jfex);
}

Play framework select input validation

I am using Play 2.1
I made a select drop down box using the helper field constructor.
The drop down box have 3 fields, default:"choose a gender", Male, and Female.
How do I ensure that the user choose one of male or female, and not the default value? (A required drop down field)
I am using Play!Framework 2.1.0, below is a simple solution for your problem:
The model should be like this: (Below is simple model for your problem)
package models;
import play.data.validation.Constraints;
public class Gender {
// This field must have a value (not null or not an empty string)
#Constraints.Required
public String gender;
}
The controller should be like this:
/** Render form with select input **/
public static Result selectInput() {
Form<Gender> genderForm = Form.form(Gender.class);
return ok(views.html.validselect.render(genderForm));
}
/** Handle form submit **/
public static Result validateSelectInput() {
Form<Gender> genderForm = Form.form(Gender.class).bindFromRequest();
if (genderForm.hasErrors()) { // check validity
return ok("Gender must be filled!"); // can be bad request or error, etc.
} else {
return ok("Input is valid"); // success validating input
}
}
The template/view should be like this:
#(genderForm: Form[models.Gender])
#import views.html.helper._
#main(title = "Validate Select") {
#form(action = routes.Application.validateSelectInput()) {
#********** The default value for select input should be "" as a value *********#
#select(
field = genderForm("gender"),
options = options("" -> "Select Gender", "M" -> "Male", "F" -> "Female")
)
<input type="submit" value="Post">
}
}
See also this post as reference : Use of option helper in Play Framework 2.0 templates

Strange "Expected invocation on the mock at least once, but was never performed" error when I am setting up the Mock

I'm getting this error from Moq via NUnit, and it doesn't make much in the way of sense to me.
"Expected invocation on the mock at least once, but was never performed: x => x.DeleteItem(.$VB$Local_item)"
"at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable1 setups, IEnumerable1 actualCalls, Expression expression, Times times, Int32 callCount)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify[T](Mock mock, Expression1 expression, Times times, String failMessage)
at Moq.Mock1.Verify(Expression`1 expression)
at PeekABookEditor.UnitTests.ItemBrowsing.Can_Delete_Item() in C:\Projects\MyProject\MyProject.UnitTests\Tests\ItemBrowsing.vb:line 167"
Very similar code works well in C#, so the error might be minor and syntactical on my part.
Here's my code:
<Test()> _
Public Sub Can_Delete_Item()
'Arrange: Given a repository containing some item...
Dim mockRepository = New Mock(Of IItemsRepository)()
Dim item As New Item With {.ItemID = "24", .Title = "i24"}
mockRepository.Setup(Function(x) x.Items).Returns(New Item() {item}.AsQueryable())
'Act ... when the user tries to delete that product
Dim controller = New ItemsController(mockRepository.Object)
Dim result = controller.Delete(24)
'Assert ... then it's deleted, and the user sees a confirmation
mockRepository.Verify(Sub(x) x.DeleteItem(item))
result.ShouldBeRedirectionTo(New With {Key .action = "List"})
Assert.AreEqual(DirectCast(controller.TempData("message"), String), "i24 was deleted")
End Sub
The guilty line appears to be "mockRepository.Verify(Sub(x) x.DeleteItem(item))"
Any thoughts on how to fix this?
Working C# code isn't the exact same, but here it is:
[Test]
public void Can_Delete_Product()
{
// Arrange: Given a repository containing some product...
var mockRepository = new Mock<IProductsRepository>();
var product = new Product { ProductID = 24, Name = "P24"};
mockRepository.Setup(x => x.Products).Returns(
new[] { product }.AsQueryable()
);
// Act: ... when the user tries to delete that product
var controller = new AdminController(mockRepository.Object);
var result = controller.Delete(24);
// Assert: ... then it's deleted, and the user sees a confirmation
mockRepository.Verify(x => x.DeleteProduct(product));
result.ShouldBeRedirectionTo(new { action = "Index" });
controller.TempData["message"].ShouldEqual("P24 was deleted");
}
In your VB test method, you create Item with a string ItemID = "24", but you call the controller.Delete method with an integer value of 24.
Check your controller code and see if the type discrepancy results in the item not being identified correctly, so either DeleteItem is not called at all, or is called with a different Item.

AspectJ pointcuts and advice

I have to enforce a policy issuing a warning if items not belonging to a particular category are being added, apart from the three which are allowed and disallowing such additions.....
So far i am able to find the items and issue warning.... but not sure how to stop them from being added....
For Eg.
Allowed categories Shoes and socks
but if i try and add a vegetable item to the inventory it should give me a warning saying "category not allowed../nItem will not be added to inventory"..... and then proceed to the next item....
This is what i've written so far.....
pointcut deliverMessage() :
call(* SC.addItem(..));
pointcut interestingCalls(String category) :
call(Item.new(..)) && args(*, *, category);
before(String category): interestingCalls(category) {
if(category.equals("Socks")) {
System.out.println("category detect: " + category);
else if(category.equals("Shoes"))
System.out.println("category detect: " + category);
else {
check=true;
System.out.println("please check category " + category);
}
}
Why not use the around aspect instead. Then, if they are not of the correct category you don't go into that method, so it gets skipped, if the skipped method is just doing the adding.
UPDATE:
Here is an example from AspectJ In Action, by Manning Publication.
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
Object around() : publicOperation() {
long start = System.nanoTime();
Object ret = proceed();
long end = System.nanoTime();
System.out.println(thisJoinPointStaticPart.getSignature()
+ " took " + (end-start) + " nanoseconds");
return ret;
}
}
So, if you wanted to check if you should add the item, if it is an allowed category then just call proceed, otherwise you would just return a null perhaps.