Folowwing orthodox TDD, before coding the actual search() method, we implement a failed test, then write just enough code to pass the test. So we need to add a search_existing_guitar( ) method within the test class
method search_existing_guitar.
* First create some test guitars to add to inventory
initialize_inventory( ).
* Then instantiate the guitar object to search
data(attrib_search) = value zguitars( builder = 'Fender'
model = 'Stratocaster'
type = 'electric'
backwood = 'Alder'
topwood = 'Alder' ).
data(guitar_to_search) = new zcl_guitar( attrib_search ).
* Search the sample guitar into the inventory
data(found_guitar) = inventory->search( guitar_to_search ).
* Finally, determine whether the guitar was found
cl_abap_unit_assert=>assert_bound( act = found_guitar " Reference variable to be checked
msg = 'Guitar was not found' ).
endmethod.
method initialize_inventory.
data(guitar_to_add) = value zguitars( serialnumber = '11277'
price = '3999.95'
builder = 'Collings'
model = 'CJ'
type = 'acoustic'
backwood = 'Indian Rosewood'
topwood = 'Sitka' ).
data(guitar) = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = 'V95693'
price = '1499.95'
builder = 'Fender'
model = 'Stratocaster'
type = 'electric'
backwood = 'Alder'
topwood = 'Alder' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = 'V9512'
price = '1549.95'
builder = 'Fender'
model = 'Stratocaster'
type = 'electric'
backwood = 'Alder'
topwood = 'Alder' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = '122784'
price = '5495.95'
builder = 'Martin'
model = 'D-18'
type = 'acoustic'
backwood = 'Brazilian Rosewood'
topwood = 'Adriondack' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = '76531'
price = '6295.95'
builder = 'Martin'
model = 'OM-28'
type = 'acoustic'
backwood = 'Brazilian Rosewood'
topwood = 'Adriondack' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = '70108276'
price = '2295.95'
builder = 'Gibson'
model = 'Les Paul'
type = 'electric'
backwood = 'Mahogany'
topwood = 'Maple' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = '82765501'
price = '1890.95'
builder = 'Gibson'
model = 'SG 61 Reissue'
type = 'electric'
backwood = 'Mahogany'
topwood = 'Mahogany' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
guitar_to_add = value zguitars( serialnumber = '629584'
price = '2100.95'
builder = 'PRS'
model = 'Dave Navarro Signature'
type = 'electric'
backwood = 'Mahogany'
topwood = 'Maple' ).
guitar = new zcl_guitar( guitar_to_add ).
inventory->add_guitar( guitar ).
endmethod.
Let's create a simple linear search algorithm that takes in a guitar object reference, and return the found object. Nothing complicated, just iterate through all guitars, and compare each of its properties.
"! <p class="shorttext synchronized" lang="en"></p>
"! Iterates through the inventory, comparing each property of the i_guitar object with the guitar
"! inside the inventory. Returns a guitar object only if all of the properties match
"! @parameter i_guitar_to_search | <p class="shorttext synchronized" lang="en"> Guitar to search</p>
"! @parameter r_found_guitars | <p class="shorttext synchronized" lang="en">Guitar found in the inventory</p>
methods search
importing
i_guitar_to_search type ref to zcl_guitar
returning
value(r_found_guitar) type ref to zcl_guitar.
method search.
loop at guitars assigning field-symbol(<guitar>).
if <guitar>-ref->attributes-builder = i_guitar_to_search->attributes-builder
and <guitar>-ref->attributes-model = i_guitar_to_search->attributes-model
and <guitar>-ref->attributes-type = i_guitar_to_search->attributes-type
and <guitar>-ref->attributes-backwood = i_guitar_to_search->attributes-backwood
and <guitar>-ref->attributes-topwood = i_guitar_to_search->attributes-topwood.
r_found_guitar = <guitar>-ref.
return.
endif.
endloop.
endmethod.
Now, I have many problems with this implementation. First, these string comparissons are not really efficient. It only takes a typo, or a incorrect capitalized letter for the test to fail. Second, what if the owner wants his clients to have multiple choices? This search method only returns one guitar, that certainly won't make him happy. Third, the ZCL_INVENTORY and ZCL_GUITAR classes depend too much on each other.
With so many things to fix, it can be hard to know where to start. Now, this is an oversimplification of a real world problem, but i'm pretty sure you can relate to this story, and have been in the same situation. Too many things to fix, time is too short, customer wants to see results as soon as possible.
class zcl_enum_base definition public abstract create protected.
public section.
methods get_id final returning value(result) type string.
protected section.
types:
t_enum_values type hashed table of ref to zcl_enum_base with unique key table_line.
class-methods: value_by_id importing i_type_name type string
i_id type string
returning value(result) type ref to zcl_enum_base,
values_by_name importing i_type_name type string
returning value(result) type t_enum_values.
methods constructor importing i_id type string.
private section.
types: begin of s_enum,
id type string,
ref type ref to zcl_enum_base,
end of s_enum,
t_enums type hashed table of s_enum with unique key id,
begin of s_enums_by_type_name,
type_name type string,
enums type t_enums,
end of s_enums_by_type_name,
t_enums_by_type_names type hashed table of s_enums_by_type_name
with unique key type_name.
class-data: all_enums type t_enums_by_type_names.
data: id type string.
endclass.
class zcl_enum_base implementation.
method get_id.
result = to_upper( me->id ).
endmethod.
method constructor.
field-symbols:
<enums_by_tn> type s_enums_by_type_name.
data:
enums_by_tn like <enums_by_tn>,
enum like line of <enums_by_tn>-enums,
type_name type string.
me->id = i_id.
" store newly created enum in enum data model
enum-id = me->id.
enum-ref = me.
type_name = CAST cl_abap_typedescr( cl_abap_datadescr=>describe_by_object_ref( me ) )->get_relative_name( ).
read table zcl_enum_base=>all_enums with table key type_name = type_name assigning <enums_by_tn>.
if ( <enums_by_tn> is not assigned ).
enums_by_tn-type_name = type_name.
insert enums_by_tn into table zcl_enum_base=>all_enums.
assert sy-subrc = 0. " expected by this framework
read table zcl_enum_base=>all_enums with table key type_name = type_name assigning <enums_by_tn>.
assert sy-subrc = 0. " expected by this framework
endif.
insert enum into table <enums_by_tn>-enums.
assert sy-subrc = 0. " expected by this framework
endmethod.
method values_by_name.
read table zcl_enum_base=>all_enums with table key type_name = i_type_name into data(enums).
assert sy-subrc = 0. " it's ensured by framework/pattern
loop at enums-enums into data(enum).
insert enum-ref into table result.
endloop.
endmethod.
method value_by_id.
data: id type string.
read table zcl_enum_base=>all_enums with table key type_name = i_type_name into data(enums).
assert sy-subrc = 0.
id = to_upper( i_id ).
read table enums-enums with table key id = id into data(enum).
if sy-subrc <> 0.
"Raise exception unsupported
endif.
result = enum-ref.
endmethod.
endclass.
class zcl_enum_guit_type definition
public
inheriting from zcl_enum_base
final
create private .
public section.
types:
t_enum_guit_type type hashed table of ref to zcl_enum_guit_type with unique key table_line.
class-data:
acoustic type ref to zcl_enum_guit_type read-only,
electric type ref to zcl_enum_guit_type read-only.
class-methods:
class_constructor,
value_of importing i_id type string
returning value(result) type ref to zcl_enum_guit_type,
values returning value(result) type t_enum_guit_type.
protected section.
private section.
constants:
id_acoustic type string value 'ACOUSTIC',
id_electric type string value 'ELECTRIC'.
endclass.
class zcl_enum_guit_type implementation.
method class_constructor.
acoustic = new zcl_enum_guit_type( i_id = id_acoustic ).
electric = new zcl_enum_guit_type( i_id = id_electric ).
endmethod.
method values.
data concrete type ref to zcl_enum_guit_type.
data(enums) = zcl_enum_base=>values_by_name( 'ZCL_ENUM_GUIT_TYPE').
loop at enums into data(enum).
concrete ?= enum.
insert concrete into table result.
endloop.
endmethod.
method value_of.
result ?= zcl_enum_base=>value_by_id( i_type_name = 'ZCL_ENUM_GUIT_TYPE'
i_id = i_id ).
endmethod.
endclass.
TYPES: BEGIN OF elem,
id TYPE i,
type TYPE REF TO zcl_enum_guit_type,
END OF elem.
DATA(elem) = VALUE elem( id = 34
type = zcl_enum_guit_type=>acoustic ).
cl_demo_output=>display( data = elem-type->get_id( ) ).
types: begin of ty_guitar,
serial_number type z_serial_number,
ref type ref to zcl_guitar,
end of ty_guitar,
guitars_tab type hashed table of ty_guitar with unique key serial_number.
"! <p class="shorttext synchronized" lang="en"></p>
"! Iterates through the inventory, comparing each property of the i_guitar object with the guitar
"! inside the inventory. Returns a guitar object only if all of the properties match
"! @parameter i_guitar_to_search | <p class="shorttext synchronized" lang="en"> Guitar to search</p>
"! @parameter r_found_guitars | <p class="shorttext synchronized" lang="en">Guitar found in the inventory</p>
methods search
importing
i_guitar_to_search type ref to zcl_guitar
returning
value(r_found_guitars) type guitars_tab.
method search.
loop at guitars assigning field-symbol(<guitar>).
if <guitar>-ref->attributes-builder = i_guitar_to_search->attributes-builder
and <guitar>-ref->attributes-model = i_guitar_to_search->attributes-model
and <guitar>-ref->attributes-type = i_guitar_to_search->attributes-type
and <guitar>-ref->attributes-backwood = i_guitar_to_search->attributes-backwood
and <guitar>-ref->attributes-topwood = i_guitar_to_search->attributes-topwood.
data(found_guitar) = value ty_guitar( serial_number = <guitar>-ref->attributes-serialnumber
ref = <guitar>-ref ).
insert found_guitar into table r_found_guitars.
endif.
endloop.
endmethod.
types: begin of ty_guitar_attributes,
serialnumber type z_serial_number,
builder type ref to zcl_enum_builder,
model type z_guitar_model,
type type ref to zcl_enum_guit_type,
backwood type ref to zcl_enum_wood,
topwood type ref to zcl_enum_wood,
price type z_price,
currency type waers,
end of ty_guitar_attributes.
method search_existing_guitar.
* First create some test guitars to add to inventory
initialize_inventory( ).
* Then instantiate the guitar object to search
data(attrib_search) =
value zcl_guitar=>ty_guitar_attributes( builder = zcl_enum_builder=>fender
model = 'Stratocastor'
type = zcl_enum_guit_type=>electric
backwood = zcl_enum_wood=>alder
topwood = zcl_enum_wood=>alder ).
data(guitar_to_search) = new zcl_guitar( attrib_search ).
* Search the sample guitar into the inventory
data(found_guitars) = inventory->search( guitar_to_search ).
* Finally, determine whether the guitar was found
cl_abap_unit_assert=>assert_not_initial( act = found_guitars " Reference variable to be checked
msg = 'No guitars found' ).
endmethod.
Adding so many guitars manually is really painful. Lots of copy-paste, code looks cluttered. Even after hiding it under a helper method. It turns out that some smart people already thought about this, and created a very neat way of using test data in a much simpler way. But I will leave this for the next post.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
2 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |