How to get variable from feature, which is using in karate.repeat? - karate

I need to create 2 items, use get method to check if everything is ok and after that I should delete these items.
I have 1 tc - getItem, which uses 2 helpers (postItem and deleteItem).
For getItem I need to have itemId, which I get from postItem, where this variable is defined. After that I use the same itemId for deleteItem as afterhook. What I do:
Feature:get item
Background:Pre-conditions
* url apiUrl
* call read('classpath:/helpers/features/postItem.feature')
* configure afterScenario = function(){karate.call('classpath:/helpers/features/deleteItem.feature')}
Scenario: Get items
* path '/items/'
And param id = itemId
When method Get
Then status 200
It works but I create only 1 item and delete it correctly because itemId is predefined in postItem and I`m able to re-use it. I saw how to use karate.repeat from HERE but when I do the next
* def item = function(i){ return karate.call ('classpath:/helpers/features/postItem.feature')}
I`m not able to get itemId and as a result not able to delete it. Have tried to use
* print item.response
but it is "null"
So I have 2 questions:
How to get variable from postItem
How to delete each of these created items using afterHook?

May I offer some advice. I would NOT try to create helpers and re-use them like this. Please take some time to read this, and then you may understand: https://stackoverflow.com/a/54126724/143475
I would definitely not use a hook also. Please think of the people who need to maintain your test in the future, they will cry.
Here is how I would write your test. And let me repeat, it is OK to repeat some code when your are doing test automation. For a real, working example, see here.
Background:
* url apiUrl + '/items'
Scenario:
* request {}
* method post
* status 201
* path response.id
* method get
* status 200
* request {}
* method post
* status 201
* path response.id
* method delete
# and so on
Otherwise, the only thing I will say is please refer to the documents on how you can call features and get data back in a loop without using karate.repeat() which should be used only for creating JSON arrays. You can see this answer which has an example and links to the documentation: https://stackoverflow.com/a/75394445/143475

Have found solution how can I do this using DRY pattern + afterhooks.
Feature:get items
Background:Pre-conditions
* url apiUrl
* def item = function(i){ return karate.call ('classpath:/helpers/features/postItem.feature')}
* def createdItem = karate.repeat(2, item )
* table createdItems
|itemId |
|createdItem[0].response.data.id|
|createdItem[1].response.data.id|
* configure afterScenario = function(){karate.call('classpath:/helpers/features/deleteItem.feature', createdItems )}
Scenario: Get all items
* path '/items'
When method Get
Then status 200
It works, but maybe it also can be updated. Im new in this)
So, basically, what I do:
I create 2 items, for get method using karate.repeat with calling postItem feature
I create table with itemId references
Create afterHook with calling deleteItem.feature, which should have argument itemId and I provide created table for this.
And I have scenario, which checks created items
And after that these created items are deleted by afterhooks.
As a result, I have clear scenario, which contains
Pre-conditions --> creating items (preparing data)
Scenario body --> GET method
Post-conditions --> deleting created items and returning to default state.
All of this I do because dont have DB read permission) In an ideal world, preparing data should be done via SQL and deleted as well)
Hope this will help someone)) Also, if you find better solution, feel free to write this here) Tnx

Related

Calling feature file within another feature file using tags [duplicate]

We have a feature A with several scenarios. And we need one scenario from that file. Can we call it in our feature B?
No. You need to extract that Scenario into a separate*.feature file and then re-use it using the call keyword.
EDIT: Karate 0.9.0 onwards will support being able to call by tag as follows:
* def result = call read('some.feature#tagname')
To illustrate the answer from Karate-inventor Peter Thomas with an example:
Given a feature-file some.feature with multiple scenarios tagged by a tag-decorator:
#tagname
Scenario: A, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `uri`
* def uri = responseHeaders['Location'][0]
#anotherTag
Scenario: X, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `createdResourceId`
* def createdResourceId = $.id
In another feature we can call a specific scenario from that feature by its tag, e.g. tagname:
Scenario: B, another case reusing some results of A
* def result = call read('some.feature#tagname')
* print "given result of A is: $result"
Given path result.uri + '/update'
See also: demo of adding custom tags to scenarios

karate xpath - Can we replace/delete a node

I'm looking to test error messages by modifying a valid xml message file.
I can easily add nodes like this in the scenario:
* def invalidDocumentId = read('Valid.xml')
* set invalidDocumentId /soapenv:Envelope/soapenv:Body = <tis:extraBonus>Extra</tis:extraBonus>
Given request invalidDocumentId
But I'd like to also remove nodes, update node names, change attribute names.
Is there a way to set that or do I need to call Java com.intuit.karate.XmlUtils.
First, removing and adding a node and even attributes should be easy, just use the remove and set keywords with XPath. They will over-write values if needed.
* def base = <query><name>foo</name></query>
* remove base /query/name
* match base == <query/>
* set base /query/foo = 'bar'
* set base /query/#baz = 'ban'
* match base == <query baz="ban"><foo>bar</foo></query>
And the good news is that if you have some really tricky XML manipulation requirements, the string replace syntax comes to the rescue. This is best explained in this other answer on Stack Overflow: https://stackoverflow.com/a/50367134/143475 | https://stackoverflow.com/a/53682733/143475

Default values for query parameters

Please forgive me if my question does not make sense.
What im trying to do is to inject in values for query parameters
GET1 File
Scenario:
Given path 'search'
And param filter[id] = id (default value or variable from another feature file)
POST1 File
Scenario:
def newid = new id made by a post call
def checkid = read call(GET1) {id : newid}
like if one of my feature files creates a new id then i want to do a get call with the above scenario. therefore i need a parameter there which takes in the new id.
On the other hand if i do not have an id newly created or the test creating it is not part of the suite. i want to still be able to run the above mentioned scenario but this time it has a default value to it.
Instead of param use params. It is designed so that any keys with null values are ignored.
After the null is set on the first line below, you can make a call to another feature, and overwrite the value of criteria. If it still is null, no params will be set.
* def criteria = null
Given path 'search'
And params { filter: '#(criteria)' }
There are multiple other ways to do this, also refer to this set of examples for data-driven search params: dynamic-params.feature
The doc on conditional logic may also give you some ideas.

Can we call a scenario from one feature in another using karate?

We have a feature A with several scenarios. And we need one scenario from that file. Can we call it in our feature B?
No. You need to extract that Scenario into a separate*.feature file and then re-use it using the call keyword.
EDIT: Karate 0.9.0 onwards will support being able to call by tag as follows:
* def result = call read('some.feature#tagname')
To illustrate the answer from Karate-inventor Peter Thomas with an example:
Given a feature-file some.feature with multiple scenarios tagged by a tag-decorator:
#tagname
Scenario: A, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `uri`
* def uri = responseHeaders['Location'][0]
#anotherTag
Scenario: X, base case that shares results to e.g. B
// given, when, then ... and share result in a map with keyword `createdResourceId`
* def createdResourceId = $.id
In another feature we can call a specific scenario from that feature by its tag, e.g. tagname:
Scenario: B, another case reusing some results of A
* def result = call read('some.feature#tagname')
* print "given result of A is: $result"
Given path result.uri + '/update'
See also: demo of adding custom tags to scenarios

Magento Bulk update attributes

I am missing the SQL out of this to Bulk update attributes by SKU/UPC.
Running EE1.10 FYI
I have all the rest of the code working but I"m not sure the who/what/why of
actually updating our attributes, and haven't been able to find them, my logic
is
Open a CSV and grab all skus and associated attrib into a 2d array
Parse the SKU into an entity_id
Take the entity_id and the attribute and run updates until finished
Take the rest of the day of since its Friday
Here's my (almost finished) code, I would GREATLY appreciate some help.
/**
* FUNCTION: updateAttrib
*
* REQS: $db_magento
* Session resource
*
* REQS: entity_id
* Product entity value
*
* REQS: $attrib
* Attribute to alter
*
*/
See my response for working production code. Hope this helps someone in the Magento community.
While this may technically work, the code you have written is just about the last way you should do this.
In Magento, you really should be using the models provided by the code and not write database queries on your own.
In your case, if you need to update attributes for 1 or many products, there is a way for you to do that very quickly (and pretty safely).
If you look in: /app/code/core/Mage/Adminhtml/controllers/Catalog/Product/Action/AttributeController.php you will find that this controller is dedicated to updating multiple products quickly.
If you look in the saveAction() function you will find the following line of code:
Mage::getSingleton('catalog/product_action')
->updateAttributes($this->_getHelper()->getProductIds(), $attributesData, $storeId);
This code is responsible for updating all the product IDs you want, only the changed attributes for any single store at a time.
The first parameter is basically an array of Product IDs. If you only want to update a single product, just put it in an array.
The second parameter is an array that contains the attributes you want to update for the given products. For example if you wanted to update price to $10 and weight to 5, you would pass the following array:
array('price' => 10.00, 'weight' => 5)
Then finally, the third and final attribute is the store ID you want these updates to happen to. Most likely this number will either be 1 or 0.
I would play around with this function call and use this instead of writing and maintaining your own database queries.
General Update Query will be like:
UPDATE
catalog_product_entity_[backend_type] cpex
SET
cpex.value = ?
WHERE cpex.attribute_id = ?
AND cpex.entity_id = ?
In order to find the [backend_type] associated with the attribute:
SELECT
  backend_type
FROM
  eav_attribute
WHERE entity_type_id =
  (SELECT
    entity_type_id
  FROM
    eav_entity_type
  WHERE entity_type_code = 'catalog_product')
AND attribute_id = ?
You can get more info from the following blog article:
http://www.blog.magepsycho.com/magento-eav-structure-role-of-eav_attributes-backend_type-field/
Hope this helps you.