When(/^I (?:create|add)\s?\w* payment\s((?:with|to) conversion)?\s?using options:$/) do |with_conversion, options|
@options_for_pay_create = options.hashes.first.symbolize_keys
params_for_removal = @options_for_pay_create.reject{|k,v| v != 'remove'}.keys
@options_for_pay_create[:conversion_id] = @conversion_id if @conversion_id
beneficiary_options = {}
if @options_for_pay_create.has_key?(:on_behalf_of)
if @options_for_pay_create[:on_behalf_of] =~ (/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]*\Z/)
@on_behalf_of = @options_for_pay_create[:on_behalf_of]
else
client_type = @options_for_pay_create[:on_behalf_of]
@on_behalf_of = (TCC_ENV[:users][client_type]).contact_id
end
@options_for_pay_create[:on_behalf_of] = @on_behalf_of
beneficiary_options[:on_behalf_of] = @on_behalf_of unless @beneficiary_id
end
unless @beneficiary_id
beneficiary_options.merge!({:currency => @options_for_pay_create[:currency], :bank_country => 'GB'})
@beneficiary_id = V2ApiServiceHelper.create_beneficiary(OptionsHelper.options_for_beneficiary_api_v2(beneficiary_options))['id']
end
@options_for_pay_create.merge!(:beneficiary_id => @beneficiary_id) if @beneficiary_id
@options_for_pay_create = OptionsHelper.options_for_payment(@options_for_pay_create)
if with_conversion
@conversion_id = @options_for_pay_create.fetch(:conversion_id, V2ApiServiceHelper.create_conversion(OptionsHelper.options_for_conversion)['id'])
@options_for_pay_create.merge!({:conversion_id => @conversion_id})
end
@options_for_pay_create.each do |k, v|
@options_for_pay_create.delete(k) if v == 'remove'
@options_for_pay_create[k] = SecureRandom.random_number(999999).to_s if v == 'random'
@options_for_pay_create[k] = SecureRandom.hex(10).to_s if v == 'random_string'
end
params = OptionsHelper.options_for_payer(@options_for_pay_create).merge(@options_for_pay_create)
if params['payer_entity_type']
params.delete('payer_type')
else
params['payer_entity_type'] = params.delete('payer_type') if params['payer_type']
end
params.delete_if{|k,_| params_for_removal.include?(k.to_sym)}
params["payment_date"] = (Date.today).strftime('%Y-%m-%d') if params["payment_date"] == 'today'
conversion_date = @conversion ? Date.parse(@conversion["conversion_date"]) : nil
params["payment_date"] = (StaticDataServiceHelper.get_next_date_of('Saturday', conversion_date)).strftime('%Y-%m-%d') if params["payment_date"] == 'Saturday'
@v2_response = V2ApiServiceHelper.create_payment(params)
@options_for_pay_create.delete(:on_behalf_of)
@payment = @v2_response
@payment_id = @v2_response['id']
@empty_timestamps = ['transferred_at']
@payer_id = @v2_response['payer_id']
@payments_for_submissions ||= []
@payments_for_submissions << @payment_id
@response = @v2_response
end
Test author has to provide around 15!!! parameters to create beneficiary, payments etc.
Most of them are not “interesting” for particular scenario
Existing code for generation of required options is total mess and is not maintainable at all
Hard to read big tables in scenarios
And I create payment using options:
| currency | amount | reason | reference | payment_type | payer_entity_type | payer_address | payer_city |..
| <currency> | 200 | Test | paymentgroup 1 | regular | individual | 221b, backer street | London |..
..| payer_country | payer_first_name | payer_last_name | payer_date_of_birth | payer_identification_type | payer_identification_value |
..| GB | John | Smith | 1990-11-11 | passport | 123456 |
They are:
Hard to maintain
Hard to find method you want (if one is not found duplicate methods are created)
They are not only "transport" classes including only methods with calls, but also helper methods that should not be there:
Service helpers include too many methods
Example:
PaymentsServiceHelper class contains 136 methods
They are randomly added without any grouping
Many of those methods are not used at all
def self.create_settlement_in_status_part_paid(principal_identifier)
conversions = []
options_for_conversion = OptionsHelper.options_for_conversion
conversions << V2ApiServiceHelper.create_conversion(options_for_conversion)
options_for_conversion = options_for_conversion.merge({:buy_currency => options_for_conversion[:sell_currency],
:sell_currency => options_for_conversion[:buy_currency]})
conversions << V2ApiServiceHelper.create_conversion(options_for_conversion)
settlement = create_settlement_in_status_funds_arrived(conversions)
conversions.each do |conversion|
TransactronServiceHelper.close_hedges_for_trade(conversion['short_reference'], conversion['currency_pair'], conversion['buy_currency'], conversion['client_buy_amount'])
end
currency_entry = TradingServiceHelper.find_settlement_entry(settlement['short_reference'], conversions.first['sell_currency'])
TradingServiceHelper.make_entry_payment({:entry_id=>currency_entry["settlement_entry"]["uuid"], :currency => conversions.first['sell_currency'], :override_status_check => "true"}, principal_identifier)
V2ApiServiceHelper.get_settlement(settlement['id'])
end
Usage of instance variables to maintain state
No registry objects that store all the created/retrieved entities during the test
Most of step definitions include statements that share and set state in instance variables
All step definitions are very dependent on each other
Find suitable existing step definition that uses proper instance variable takes too much time
Examples:
When(/^I (?:create|add)\s?\w* payment\s((?:with|to) conversion)?\s?using options:$/) do |with_conversion, options|
:
:
@v2_response = V2ApiServiceHelper.create_payment(params)
@options_for_pay_create.delete(:on_behalf_of)
@payment = @v2_response
@payment_id = @v2_response['id']
@empty_timestamps = ['transferred_at']
@payer_id = @v2_response['payer_id']
@payments_for_submissions ||= []
@payments_for_submissions << @payment_id
@response = @v2_response
end
Then(/^I should receive the success response$/) do
@response.code.should == 200
end
Then(/^I should get successful response with created payment from API v2$/) do
@v2_response['id'].should_not be_nil
end
Hard to implement complex and flexible scenarios
We can not do things like this:
Scenario: Generation of different currency payments
Given I am signed for v2 API as Cash Manager account user
And I have beneficiary created using API V2 with options:
| currency | bank_country | payment_types |
| USD | US | regular, priority |
| GBP | GB | regular, priority |
| SGD | SG | regular, priority |
| EUR | DE | regular, priority |
When I create standalone payments with following parameters:
| currency | amount | payment_type |
| USD | 2000 | priority |
| GBP | 1000 | regular |
| SGD | 3000 | regular |
| EUR | DE | priority |
We can not create bunch of beneficiares and bunch of payments for them
All the entities are selected implicitly(e.g. from last values stored in instance variables like @beneficiary_id, @conversion_id). There is no easy way to store and retrieve them.
There is no way to specify explicitly what you want to use(e.g. what conversion for payment)
As result such scenarios take much more time to prepare them, ugly and hard to understand
Example of existing scenario
Scenario Outline: Compliance checks should be triggered after update only when payer_id or beneficiary_id had changed
Given the Compliance Engine database is clear
And I am signed in as api user
And I have beneficiary created with options:
|bank_account_holder_name |payment_types |currency |beneficiary_country |::
|GOOD GUY |priority, regular |GBP |GB |::
When I create conversion via APIv2 using options:
| buy_currency | sell_currency | amount | fixed_side | term_agreement | reason |
| GBP | USD | 7000 | buy | true | test |
And I create payment with conversion using options:
|currency|amount |reason |::
|GBP |2345.67|cucumber_test|::
And I have beneficiary created with options:
|bank_account_holder_name |payment_types |currency |beneficiary_country |::
|BAD GUY |regular |USD |GB |::
When I create conversion via APIv2 using options:
| buy_currency | sell_currency | amount | fixed_side | term_agreement | reason |
| USD | EUR | 7000 | buy | true | test |
And I create payment with conversion using options:
|currency|amount |reason |::
|USD |2345.67|cucumber_test|::
New reusable step definitions must be prepared
Mess with existing step definitions
Mess with existing feature files
References