[How TO]Write Complex Test Cases Using a DIY Test Case Generator.
What is this?
We have to write a lot of code and tests.
Sometimes, we need to maintain huge and complex code, as well as tests, that follow changes when business situations change so fast.
It's too hard to write and keep quality test code when faced with complex business logic.
We are coders. Let's fix these issues with a generator to make tests easily and quickly.
After learning this method, you can easily scale in these situations.
For Windows users
- GitHub clone
yarn
yarn watch_xlsx
- Define your complex business patterns in XLSX.
- Modify generate_test_case.rb to do what you need.
- Copy generated test code to your project.
- Run and modify data that you want.
For Non-Windows Users
- Clone from GitHub
yarn
yarn watch_csv
- Define your complex business patterns in CSV.
- Modify generate_test_case.rb to do what you need.
- Copy generated test code to your project.
- Run and modify data that you want.
Prepare to Use
Clone from GitHub
Open your terminal or command prompt.
git clone https://github.com/panghea/testcasegenerator
Example
This example is workflow for Item Packaging.
Item Packaging is defined by Item Category and Item Ship Type.
There are five routes to approve Item Shipping for the package.
Code Example
public class Routes {
// result for Package Type
public String packageType;
public void approveA(Item item) {
// approve with Item Category and Ship Type
// process data and write to the database....
// if the final approve flow record package type to this.packageType
}
public void approveB(Item item) {
// approve with Item Category and Ship Type
// process data and write to the database....
// if the final approve flow record package type to this.packageType
}
public void approveC(Item item) {
// approve with Item Category and Ship Type
// process data and write to the database....
// if the final approve flow record package type to this.packageType
}
public void approveD(Item item) {
// approve with Item Category and Ship Type
// process data and write to the database....
// if the final approve flow record package type to this.packageType
}
public void approveE(Item item) {
// approve with Item Category and Ship Type
// process data and write to the database....
// if the final approve flow record package type to this.packageType
}
public String Reject(Item item) {
// reject workflow when somthing happend at bussiness working.
// clean up data and record where to reject approve process to the database
return approveProcessName;
}
}
Here is an Excel data example (input/data_pattern.xlsx)
Item Category Code | Item Ship Type | Approve Route A | Approve Route B | Approve Route C | Approve Route D | Approve Route E | Package Type |
---|---|---|---|---|---|---|---|
Category A | Ship Type B | skip | need | skip | skip | need | Package C1 |
Category A | Ship Type C | need | need | skip | need | need | Package C2 |
Category A | Ship Type D | need | need | need | need | need | Package A2 |
Category A | Ship Type C | skip | skip | skip | skip | need | Package A3 |
Category B | Ship Type A | need | need | skip | need | need | Package C1 |
Category B | Ship Type B | skip | need | need | need | need | Package A2 |
Category B | Ship Type F | need | need | skip | need | need | Package B7 |
Category C | Ship Type B | need | need | need | need | need | Package B4 |
Category C | Ship Type A | need | need | skip | skip | need | Package A3 |
Category D | Ship Type F | skip | need | skip | need | skip | Package C2 |
Category D | Ship Type G | skip | need | skip | skip | need | Package D1 |
Category E | Ship Type A | need | skip | skip | skip | skip | Package F2 |
Here is CSV data example (input/data_pattern_csv.yaml)
It writes CSV inside YAML, which is useful for reading and editing for humans and converters.
data_csv:
- ["Category A","Ship Type B","skip","need","skip","skip","need","Package C1"]
- ["Category A","Ship Type C","need","need","skip","need","need","Package C2"]
- ["Category A","Ship Type D","need","need","need","need","need","Package A2"]
- ["Category A","Ship Type C","skip","skip","skip","skip","need","Package A3"]
- ["Category B","Ship Type A","need","need","skip","need","need","Package C1"]
- ["Category B","Ship Type B","skip","need","need","need","need","Package A2"]
- ["Category B","Ship Type F","need","need","skip","need","need","Package B7"]
- ["Category C","Ship Type B","need","need","need","need","need","Package B4"]
- ["Category C","Ship Type A","need","need","skip","skip","need","Package A3"]
- ["Category D","Ship Type F","skip","need","skip","need","skip","Package C2"]
- ["Category D","Ship Type G","skip","need","skip","skip","need","Package D1"]
- ["Category E","Ship Type A","need","skip","skip","skip","skip","Package F2"]
Generate YAML from each data file (Excel or CSV)
Generate YAML from XLSX files (Windows only)
If you are using Windows, you can generate YAML from XLSX.
cd testcasegenerator
ruby xlsx2yaml.rb
Generate YAML from CSV file
If you aren't using Windows, you can generate YAML from CSV.
cd testcasegenerator
ruby csv2yaml.rb
Output YAML file
generated/generator_input.yaml
---
- ItemCategory: Category A
ItemShipType: Ship Type B
ApproveRouteA: skip
ApproveRouteB: need
ApproveRouteC: skip
ApproveRouteD: skip
ApproveRouteE: need
PackageType: Package C1
- ItemCategory: Category A
ItemShipType: Ship Type C
ApproveRouteA: need
ApproveRouteB: need
ApproveRouteC: skip
ApproveRouteD: need
ApproveRouteE: need
PackageType: Package C2
...
Generate Test Case File from YAML file
ruby generate_test_case.rb
Generator Source
It takes just a few codes to generate test cases.
def generateTestCase(testCase)
outputCodes = []
# write method header
outputCodes.push(<<-GENERATOR.gsub(/^$/,"")
/*
* <b>Test Case Of [#{testCase["ItemCategory"]}] and [#{testCase["ItemShipType"]}]</b><br>
* <pre>
* ======================================================
* 2017/09/15 Tadayuki Tanigawa
* </pre>
* @throws Throwable
*/
@Test
public void testCase#{"%02d" % (testCase['caseNo']+ 1)}_#{testCase['ItemCategory'].gsub(/ /,"_")}_#{testCase['ItemShipType'].gsub(/[- ]/,"_")}() throws Throwable {
// prepare for test data by route
GENERATOR
);
# prepare data for each route
outputCodes.push(prepareTestData(testCase).join())
# do test methods
outputCodes.push(<<-GENERATOR
// create item
Item item = new Item();
item.setCategory("#{testCase["ItemCategory"].gsub(/.* /,"")}");
item.setShipType("#{testCase["ItemShipType"].gsub(/.* /,"")}");
// call each route
Routes routes = new Routes();
GENERATOR
)
outputCodes.push(testRouteCall(testCase).join())
# assert test result
outputCodes.push(<<-GENERATOR
// assert package type
assertThat("Package Type", routes.packageType, is("#{testCase["PackageType"].gsub(/.* /,"")}"));
GENERATOR
)
outputCodes.push(<<~GENERATOR
#{testCase["Assertions"]}
GENERATOR
)
outputCodes.push(<<-GENERATOR
}
GENERATOR
)
outputCodes
end
Generated Code
This is a generated test method by generate_test_case.rb.
If you have to add routes to your business rules, it's easy to create tests using a generator.
You just add a new item route as a row into XLSX or CSV.
/*
* <b>Test Case Of [Category B] and [Ship Type F]</b><br>
* <pre>
* ======================================================
* 2017/09/15 Tadayuki Tanigawa
* </pre>
* @throws Throwable
*/
@Test
public void testCase07_Category_B_Ship_Type_F() throws Throwable {
// prepare for test data by route
// For Route A insert or update data to the Database
dao.update("Update ItemRouteTable ... RouteName = 'ApproveRouteA' ,itemCategory = 'Category B'....");
// For Route B insert or update data to the Database
dao.insert("Insert RouteTable ... SELECT 'B' AS RouteType ,'Category B' as ItemCategory....");
dao.update("Update ItemCategoryTable ... itemCategory = 'Category B Where itemShipType = 'Ship Type F'")
dao.update("Update ItemRouteTable ... RouteName = 'ApproveRouteB' ,itemCategory = 'Category B'....");
// For Route D insert or update data to the Database
dao.insert("Insert RouteTable ... SELECT 'D' AS RouteType ,'Category B' as ItemCategory....");
dao.update("Update ItemRouteTable ... RouteName = 'ApproveRouteD' ,itemCategory = 'Category B'....");
// For Route E insert or update data to the Database
dao.insert("Insert RouteTable ... SELECT 'E' AS RouteType ,'Category B' as ItemCategory....");
dao.update("Update ItemRouteTable ... RouteName = 'ApproveRouteE' ,itemCategory = 'Category B'....");
// create item
Item item = new Item();
item.setCategory("B");
item.setShipType("F");
// call each route
Routes routes = new Routes();
// approve by A
route.approveA(item);
// approve by B
route.approveB(item);
// approve by D
route.approveD(item);
// approve by E
route.approveE(item);
// assert package type
assertThat("Package Type", routes.packageType, is("B7"));
}
Automatically Generate Code When You Save XLSX or CSV
It watches file modification and executes Ruby automatically.
You just edit generate_test_case.rb or data_pattern.xlsx or data_pattern_csv.yaml!
For Windows
npm install -g yarn
yarn watch_xlsx
For Others
npm install -g yarn
yarn watch_csv
Next
-
You can add custom expressions that treat data or assertion of test code into YAML for really complex tests.
- ItemCategory: Category A ItemShipType: Ship Type B .... Assertions: |1- // assert Asset Type of Category A assertThat("Asset Type", item.getAssetType(), is("23"));
Change generate_test_case.rb
outputCodes.push(<<~GENERATOR #{testCase["Assertions"]} GENERATOR )
Then generate code:
// assert Asset Type of Category A assertThat("Asset Type", item.getAssetType(), is("23"));
You can define YAML rules and generate results easily.
-
You can write Excel documents from YAML this way! It's really useful!
If you are interested, CALL ME!
FAQ
-
Why use YAML for generation?
- YAML is easier to modify than CSV and XLSX in Text Editor.
- YAML is a text file that's easy to manage on Git.
- If XLSX or CSV is broken, we can re-generate from YAML to each file — just make a generator for XLSX or CSV.
- YAML reads faster then XLSX from Ruby. We sometimes deal with big size XLSX files.
-
Why use Ruby?
- Ruby's here document is awesome for code generation.
- Ruby is good for Excel automation so far.