-1

Scenario : send post request until a condition meet but maximum 5 times otherwise fail the script

step1: get one data from database

step2: get 2nd data from db based on step one result(if step 2 gives null then start again from step 1 and maximum occurrence can be 5 times)

step3: prepare a soap request and put 1st and 2nd data as input in the request body which we got in step 1 and 2.

Step4: send a soap request and print the response

step5: validate if response.id >5000(if this condition doesn't fulfill repeat the process from step1 to step 5 but again repeat only till 5 times and if still condition doesn't fulfill till 5th time then fail the test case)

Note:- I tried with retry until as well but looks like it keeps sending the step 5 only so it will not change the request body data which we need to change after every run from step 1 to step 5.

Also, I researched a lot and came across many similar questions but couldn't achieve my desired result.

Condition based on response value in karate

Scenario: GetCountriesWithId
    * def RandomNumber =
      """
      function() {
      var count = 1
       while (count<=5) {
        var RandomNumber = db.readRows("select trunc(dbms_random.value(10000000, 10010000)) min, trunc(dbms_random.value(10200000, 10250000)) max from dual")
        karate.log('RandomNumber->',RandomNumber)
        var HolderFromDB = db.readRows("SELECT productId, userId from table1 WHERE id between "+RandomNumber[0].MIN+" and "+RandomNumber[0].MAX+" ")
          if (HolderFromDB.length >= 1) {
            karate.log('condition satisfied, exiting');
            return HolderFromDB;
          }
          count++
        }
      }
      """
    * def HolderFromDB = call RandomNumber
    * def productId = HolderFromDB.productId
    * def userId = HolderFromDB.userId
    * def javaclass = Java.type('epos.positionmanagement.service.test.GetSpendingLimitRequestTest')
    * def map = {deviceTypeId: 2, entityId: '#(defaultEntity)', ppr_id: '#(productId)', userId: '#(userId)' }
    * def createGetSpendingLimitRequest = javaclass.getPayload(map)
    Then print 'Request body--->', createGetSpendingLimitRequest
    Given request createGetSpendingLimitRequest
    When soap action 'Request'
    Then status 500
    Then print '\n', response
    * def revisionId = get response //GetSpendingLimitResponse/spendingLimit/totalAmount*1
    * def result = revisionId<=50000 ? karate.call('test1.feature') : {}
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • 1
    sorry I give up. random numbers from DB queries is something I feel you shouldn't do at all. all the best. I see a lot of bad practices here, JS loops and Java code. – Peter Thomas Nov 15 '21 at 18:21
  • 1
    This is bad test design. So many smells here. Please, for the sake of the next person who looks at your code, don't do this. Tests should ALWAYS be deterministic (if I enter "X" in a field, then "X" should be sent to the backend, etc.). Random numbers for loop control are a huge problem, and retries indicate bad test design or bad environment setup. Tests should also be as simple as possible, even if that means writing more tests to cover other test cases (although you should try to do that through data-driven mechanisms, but NOT using randoms). – user3486382 Nov 15 '21 at 18:50
  • I haven't design this test case but I am just migrating the same steps which is already present in SoapUI pro version. I am trying to replicate all the steps as it is so user can understand the test flow in karate then I can definitely improve the framework. – Rohit Kumar Nov 15 '21 at 19:48
  • And in SoapUI pro(ReadyAPI) tool it is really easy to loop to the pervious steps based on some condition. Hence I was trying to replicate same steps in karate so existing user can understand the flow and at the same time they can learn karate simultaneously. And also this is not the exact test flow I just gave one query as example for random number but in actual it is some other big query from banking table so it cant be avoided . I used these query as example to explain the flow. – Rohit Kumar Nov 15 '21 at 19:48
  • @RohitKumar in my opinion that tool has made you fall into bad programming practices. it is very hard to explain all the things wrong with your test design. my suggestion is that you work closely with someone who is a good programmer and re-write your entire flow. and PLEASE read this first: https://stackoverflow.com/a/54126724/143475 - and sorry, Karate is not designed to port tests from ReadyAPI - so please look for another tool if needed or convince your leadership (recommended) that you have to re-factor tests or drop some existing tests – Peter Thomas Nov 16 '21 at 02:53
  • @PeterThomas thanks for the suggestion I will try to improve the flow but now I dont think any improvement can be done as we are totally dependent on customer data which resides in DB and we have to find the exact customer which have all the criteria to test this service and these customers keep on changing in DB so we have to approach this random number as we have millions of customer and to find a appropriate customer daily is not possible if we hard code the data. – Rohit Kumar Nov 17 '21 at 11:54
  • @PeterThomas And the javaclasses I am using to create xml request which is nothing but the actual services in our server and I have added these services as pom dependency in my project and fetching the data from DB and filling all customer details in the request xml by passing a map value. Though I really want to improve this framework and I will definitely improvise this project as per your suggestion. And yes I have resolved this problem as explained below in my answer. And really thanks for this wonderful Karate framework. – Rohit Kumar Nov 17 '21 at 11:54

2 Answers2

1

I think this test case should not be automated. Because it is not clear and it has a lot of dependent situation. You should take principle for making each test case standalone and independent. Also Test cases should have been good designed so that you can easily develop a code for them. Otherwise even if you have achieved to automate them, they will be most probably flaky tests and you will spend more time to fix them instead of testing them manually. Think twice, think scenarios once more also and decide your next move. Also please take a look this business logic which mentioned in your scenarios once more since it doesn't look so logical.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Selcuk
  • 31
  • 2
-1

I have resolved the issue as below:-

Put the steps till post request in another feature file as below:-

@ignore
Feature: Reusable
 
  Background: Maintenance Soap Request
    * def DbUtils = Java.type('util.DbUtils')
    * def db = new DbUtils(eposEnvironment.database[defaultEntity])
    * url eposEnvironment.baseUrl
 
  @Limit-GTC-Straddle @parallel=false
  Scenario: Limit-GTC-Straddle
    * def derivativeOrderTemp =
    """
      function() {
          var count = 1
          while (count <= 10) {
              var selectRandomLetter = db.readRows("select chr(trunc(dbms_random.value(65,74))) firstLetter, chr(trunc(dbms_random.value(80,90))) lastLetter, trunc(dbms_random.value(1, 4)) positionToBuy, to_char(current_date + 364, 'YYYY-MM-DD') expDate from dual")
              var symbol1FromDB = db.readRows("select * from   (select be_symbool, RTRIM(be_muntsoort) as be_muntsoort,   be_code, be_optietype, be_expiratiedatum as be_expiratiedatum2,   to_char(to_date(be_expiratiedatum, 'YYYYMMDD'), 'YYYY-MM-DD') as be_expiratiedatum, be_exerciseprijs,   brs_naam, be_bv_beurs,   round(max (quot_laat) keep (dense_rank last order by quot_datum)+ 1, 0) as limit,   (round(max (quot_laat) keep (dense_rank last order by quot_datum)+ 1, 0)*100*-1*" + selectRandomLetter[0].POSITIONTOBUY + ") effAmount,   be_volgnummer   from  beleggingsinstrument     join fn_quotes_table   on quot_symbool = be_symbool   and quot_optietype = be_optietype   and quot_expiratiedatum = be_expiratiedatum   and quot_exerciseprijs = be_exerciseprijs     join beurzen   on brs_nummer = be_bv_beurs     where be_Symbool in     (select be_symbool     from beleggingsinstrument     where be_bi_nummer = 100     and length(trim(be_symbool)) < 4     and substr(trim(be_symbool),1,1) between '" + selectRandomLetter[0].FIRSTLETTER + "' and '" + selectRandomLetter[0].LASTLETTER + "'     and be_geblokkeerd = 0     and be_handelen_toegestaan = 1     and be_referentiesymbool = ' ')   and be_optietype = 'CALL'   and be_handelen_toegestaan = 1   and be_expiratiedatum > to_char(sysdate, 'yyyymmdd')   group by be_symbool, be_muntsoort, be_code, be_optietype, be_expiratiedatum, be_exerciseprijs, be_bv_beurs, quot_laat, brs_naam, quot_laat, quot_datum, be_volgnummer   ) where rownum = 1")
              karate.log('Symbol1FromDB-->', symbol1FromDB)
              if (symbol1FromDB.length >= 1) {
                  karate.log('condition satisfied, exiting from first conditionalGoto1 loop counter');
                  var indirectCosts1FromDB = db.readRows("select icf_cfcu_id, cfcu_description, decode(icf_amount, null, 0, icf_amount) icf_amount, decode(icf_currency, null, 'NULL', icf_currency) icf_currency, decode(icf_percentage, null, 0, icf_percentage) icf_percentage from EPP_OWNER.INDIRECT_COSTS_PER_FUND join epp_owner.calculationrules on cfcu_id = icf_cfcu_id where icf_be_id = " + symbol1FromDB[0].BE_VOLGNUMMER + " and (icf_import_date_till > to_char(sysdate, 'yy-mm-dd') or icf_search_date_till is null) and icf_ex_ante_ex_post_ind = 'A' and icf_cfcu_id in (67)")
                  karate.log('indirectCosts1FromDB-->', indirectCosts1FromDB)
                  var symbol2FromDB = db.readRows("select * from   (select be_symbool, RTRIM(be_muntsoort) as be_muntsoort,   be_code, be_optietype,   to_char(to_date(be_expiratiedatum, 'YYYYMMDD'), 'YYYY-MM-DD') as be_expiratiedatum, be_exerciseprijs,   brs_naam, be_bv_beurs,   round(max (quot_laat) keep (dense_rank last order by quot_datum)+ 1, 0) as limit,   (round(max (quot_laat) keep (dense_rank last order by quot_datum)+ 1, 0)*100*-1*" + selectRandomLetter[0].POSITIONTOBUY + ") effAmount,   be_volgnummer   from  beleggingsinstrument     join fn_quotes_table   on quot_symbool = be_symbool   and quot_optietype = be_optietype   and quot_expiratiedatum = be_expiratiedatum   and quot_exerciseprijs = be_exerciseprijs     join beurzen   on brs_nummer = be_bv_beurs     where be_Symbool = '" + symbol1FromDB[0].BE_SYMBOOL + "'   and be_optietype = 'PUT'   and be_handelen_toegestaan = 1   and be_expiratiedatum = '" + symbol1FromDB[0].BE_EXPIRATIEDATUM2 + "'   group by be_symbool, be_muntsoort, be_code, be_optietype, be_expiratiedatum, be_exerciseprijs, be_bv_beurs, quot_laat, brs_naam, quot_laat, quot_datum, be_volgnummer   ) where rownum = 1")
                  karate.log('Symbol2FromDB-->', symbol2FromDB)
                  var indirectCosts2FromDB = db.readRows("select icf_cfcu_id, cfcu_description, decode(icf_amount, null, 0, icf_amount) icf_amount, decode(icf_currency, null, 'NULL', icf_currency) icf_currency, decode(icf_percentage, null, 0, icf_percentage) icf_percentage from EPP_OWNER.INDIRECT_COSTS_PER_FUND join epp_owner.calculationrules on cfcu_id = icf_cfcu_id where icf_be_id = " + symbol2FromDB[0].BE_VOLGNUMMER + " and (icf_import_date_till > to_char(sysdate, 'yy-mm-dd') or icf_search_date_till is null) and icf_ex_ante_ex_post_ind = 'A' and icf_cfcu_id in (67)")
                  karate.log('indirectCosts2FromDB-->', indirectCosts2FromDB)
                  var selectRandomNumber = db.readRows("select trunc(dbms_random.value(10020000, 10050000)) min, trunc(dbms_random.value(11000000, 11500000)) max from dual")
                  karate.log('selectRandomNumber-->', selectRandomNumber)
                  var holderFromDB = db.readRows("SELECT * from   (select ap_relatienr, ap_rekeningnr,   hpp_holdernummer, ppr_id   from aktuele_posities ap     join epp_owner.rekeningen_per_product   on rpp_relatienummer = ap.ap_relatienr   and rpp_rekening_nummer = ap.ap_rekeningnr   and rpp_rekeningsoort = ap.ap_rekening_soort     join epp_owner.holders_per_product   on hpp_relatienummer = rpp_relatienummer   and hpp_product = rpp_productnummer   and hpp_product_volgnr = rpp_volgnr_per_product   and hpp_type_holder = 1       join rekeningen   on re_rekening = rpp_rekening_nummer   and re_nummer = rpp_relatienummer   and re_rekening_status = 2       join epp_owner.wwwusers   on wus_holder = hpp_holdernummer   and wus_userblocked = 0   join producten_per_relatie   on ppr_relatienummer = rpp_relatienummer   and ppr_productnummer = rpp_productnummer   and ppr_volgnr_per_product = rpp_volgnr_per_product    JOIN on_line_dossier onld   ON onld.onld_relatienummer = hpp_relatienummer   and onld_productnummer = rpp_productnummer    join kennis_per_client a   on rpp_relatienummer = ke_clientnr     where rpp_productnummer = 100   and rpp_volgnr_per_product = 1   and ap.ap_rekening_soort = 1000   and ap_saldo_positie > 7000   AND ap.ap_relatienr between " + selectRandomNumber[0].MIN + " and " + selectRandomNumber[0].MAX + "   and onld.onld_klasse = 16   and onld.onld_ontvangstdatum <> '00000000'   and onld.onld_vervaldatum = '00000000'   and ((ke_kennis_niv = 'V' or ke_ervaring = 'V')   and ke_mifcat_id = 25)   and not exists    (select 1      from kennis_per_client b       where (ke_kennis_niv = 'O' or ke_ervaring = 'O')       and a.ke_clientnr = b.ke_clientnr)   and rpp_relatienummer in     (select cl_nummer     from clienten     where cl_nummer between " + selectRandomNumber[0].MIN + " and " + selectRandomNumber[0].MAX + "     and cl_geb_datum < to_char(add_months(sysdate, -216), 'YYYYMMDD'))   and not exists     (select 1     from profiel_toevoeging_verwijderen     where ptv_relatie = ap.ap_relatienr)   and not exists     (select 1     from rekeninghouders_details     where UPPER(eor_compliancecode) like 'INSIDER%'     and eor_partij_id = hpp_holdernummer)   ) where rownum = 1")
                  karate.log('holderFromDB-->', holderFromDB)
                  while (count <= 10) {
                      if (holderFromDB.length >= 1) {
                          karate.log('condition satisfied, exiting from conditionalGoto2');
                          productId = holderFromDB[0].get('PPR_ID')
                          userId = holderFromDB[0].get('HPP_HOLDERNUMMER')
                          return {
                              productId,
                              userId
                          };
                      }
                      count++;
                      if (count >= 9) {
                          karate.fail('Count exceeded more than 10 times and conditionalgoto2 loop counter failed')
                      }
                  }
                  count++;
                  if (count >= 9) {
                      karate.fail('Count exceeded more than 10 times and conditionalgoto1 loop counter failed')
                  }
              }
          }
      }
    """
    * def derivativeTemp = call derivativeOrderTemp
    * def productId = derivativeTemp.productId
    * print 'Product Id is-->',productId
    * def userId = derivativeTemp.userId
    * print 'User Id is-->',userId
                        ################GetSpendingLimit###########################
    * def javaclass = Java.type('epos.positionmanagement.service.test.GetSpendingLimitRequestTest')
    * def map = {deviceTypeId: 2, entityId: '#(defaultEntity)', ppr_id: '#(productId)', userId: '#(userId)' }
    * def createGetSpendingLimitRequest = javaclass.getPayload(map)
    Then print 'Request body--->', createGetSpendingLimitRequest
    Given request createGetSpendingLimitRequest
    When soap action 'Request'
    Then print '\n', response

And then call this feature file from other test case feature file as below :-

@Ordering
Feature: CreateCombinationDerivativeOrderTemp
 
  Background: Ordering Soap Request
    * url eposEnvironment.baseUrl
 
  @parallel=true
  Scenario: Limit-GTC-Straddle
    * call read('reusable.feature@Limit-GTC-Straddle')
    * def revisionId = get response //GetSpendingLimitResponse/spendingLimit/totalAmount*1
#    * eval for(var i=0; i<=10; i++) (revisionId<3000 && i<10 ? karate.call('request1.feature') : karate.fail('Condition did not fulfill in 10 occurrence hence failing the test case'))
    * def condionalgoto3 =
    """
    function() {
        for (var i = 0; i <= 10; i++) {
            if (revisionId < 3000) {
                karate.call('request1.feature')
                if (i >= 9) {
                    karate.fail('Count exceeded more than 10 times and conditionalgoto3 loop counter failed')
                }
            }
        }
    }
    """
    * call condionalgoto3

Note :- These java classes I am using to create a payload xml which was requirement of the project and these java classes are nothing but the actual services which I have added as pom dependency in my project which directly gives me the payload xml request and I just create a map and fill the value in the request body so these java classes are mandatory to use.And also these db queries are being used to fetch the actual customer details from a database to prepare the request body so there is nothing which I can ignore or improve in this test case but yes in some queries they are using some random values to fetch some customer data that I can definitely use some Java/Js/Karate.range() but apart from that I really dont think I can improve anything here.

  • I can confidently say that this is one of the worst Karate tests I've ever seen. I hope you will re-read what all the commenters have said and re-consider your approach. Re-using Java classes from your production code is one of the biggest mistakes - because you will never know if a consumer breaks. Please have some empathy for those who have to maintain the tests in future. To anyone reading this - please don't write tests like this. I have nothing further to say. – Peter Thomas Nov 17 '21 at 13:45
  • 1
    @PeterThomas Yes, I totally agree with you that these est cases are absolutely pathetic and i had raised the some concern when I saw it first but my client wants it to recreate the same flow as it is in ReadyAPI so I had to do it in same way..and these are the exactly same steps how it is designed in ReadyAPI. But in future I will be definitely recreate these test case in simple and easiest way without any data dependency from DB and without using these java classes. – Rohit Kumar Nov 18 '21 at 08:15
  • My honest opinion is that you have a responsibility to your client to do the right thing, or educate them to do the right thing. Feel free to point them to this discussion if it helps. One of the reasons I don't like this is that once you leave, the new engineers who find themselves faced with this code will blame it on Karate and tell all their friends and family that Karate is a horrible tool. And trust me, I've seen that happen - especially in teams where the developers were asked to use Karate but they had some obsession with Java. I hope this is not the case here. – Peter Thomas Nov 18 '21 at 08:42