Section 3: The Test project

In the previous sections we saw how to create the OTC Script files. In this section, we will see how to use the downloaded test-project to compile the script files and generate the object-conversion source code and execute them. The "Getting Started" section covered details on setting up the "Test project" available in the download.

The test classes and sample classes:


Note: There are 2 projects in the downloaded zip file - the otc-test and the otc-test-schema

  1. The otc-test project:

    There are a few test classes in the otc-test project with each of them having a different focus which are explained below. Below is a screenshot of the folder structure of the "otc-test" project.

    OTC script editor

    1. The "unittest.CompilerTest.java" compiles the *.otcs files, generates all required object-conversion code. Details are given in the below topic "Unit Test classes".
    2. Before running any test class, update the "./src/test/resources/config/otc.yaml" file and set the right values for the "compiler.paths.sourceCodeDirectory:", "compiler.paths.tmdDirectory:" and the "compiler.filterPackages:" properties.
    3. The "unittest.ExecutorTest.java" executes the object conversion code present in the package name specified during execution. Please go through the code to grasp what is explained here. Details are given in the below topic "Unit Test classes".
    4. The "benchmark.jmh.otc.JmhTest.java" class tests the performance of the OTC framework.
    5. The "benchmark.jmh.mapstruct.JmhTest.java" tests the performance of the Mapstruct framework.

  2. The otc-test-schema project:
    1. There are a few example classes from the "otc-test-schema" project used in the OTCS test-case files.
    2. One of them is the "testcase.examples.converter.TestConverter.java" used in the OTC Script "execute" command example.
    3. The other one is the "testcase.examples.helper.TestFactoryHelper.java" used as an example while trying out test cases using "overrides:". Details in the "Overrides" section.
    4. The "com.kronos.airlines.dto.KronosAirlinePassenger" and the com.athena.airlines.dto.AthenaAirlinePassenger and their child classes are used as source and target types in the test cases.
    5. All these classes are annotated with JAXB annotations only for the purpose of generating XML output for demonstration purposes and to validate them with the test cases. This has got nothing to do with the OTC framework but rather any POJO is a suitable candidate for the OTC framework.

Unit Test classes:

  1. "unittest.CompilerTest" -
    1. This Junit-test class has a test method which calls the "otcsCompiler.compileOtcsFiles()" method.
    2. The "otcsCompiler.compileOtcsFiles()" method call triggers a scan of the "${OTC_HOME}/otcs-unittest/" folder and sub-folders searching for "*.otcs" files if any, and compiles them if found and then generates the source-code.
    3. After this method is executed successfully, you will find the generated source code files in the directory path that is set for the "compiler.paths.sourceCodeDirectory:" property in the "./resources/otc.yaml" file.
    4. If you did not set this "compiler.paths.sourceCodeDirectory:" property, then the files will be generated in "${OTC_HOME}/src" folder. And in that case, you will have to manually copy the "*.java" files into your project's "src" folder.
    5. The same goes with the "compiler.paths.tmdDirectory:" property as well. If this property is not set, then you will have to manually copy the "${OTC_HOME}/tmd" folder into your project's "resources" folder.
    6. The "sourceCodeCompiler.compileSourceCode()" method call compiles the generated source code.

    Note: The "Compile" button on the OTC Script editor UI also does exactly what this test class does.

  2. "unittest.ExecutorTest"
    1. This Junit-test class first creates an instance of "OtcExecutor" by calling "OtcExecutorImpl.getInstance()" method.
    2. When an instance of OtcExecutorImpl is created, it internally calls the "otcRegistry.register()" method to register some metadata files in the "tmd/" folder generated by the framework for internal use. These files are created when the "*.otcs" files are compiled.
    3. There are 2 test methods in this test class viz. "runCopyFromSourceObjectTestCases()" which converts a source object and literals to a target object, and, the "runCopyFromLiteralsTestCases()" method which creates the target object using only the literals passed in the .otcs file.
    4. Each of these methods invokes either the "otcExecutor.copyFromSource(...)" or the "otcExecutor.copyFromLiterals(...)" methods based on need.
    5. Finally, after the conversion, the output of the new target object is printed. You may choose between the XML or JSON formats.

Visibility aspects:
Note: It is very important to understand the visibility aspects of the './otc.yaml' configuration file and the './tmd' folder in different scenarios as explained below -

  1. When the OTC Script Editor needs to read the './otc.yaml' file.
    The OTC Script editor needs to load this file, and it will always look for "${OTC_HOME}/config/otc.yaml" file. So make sure you update the parameters in this file so that the editor can pick up the values.

    Secondly, when you click on the "Compile" button on the OTC Script Editor UI (refer to the section "OTC Script Editor"), or, when you invoke the compilation by the test-class "CompilerTest" (which is explained below), the "*.tmd" files are created. These files are meant for internal use by the "unittest.ExecutorTest" class or any other test class in your project which does the same. Therefore, the 'compiler.paths.tmdDirectory:' folder location that you should configure in the 'otc.yaml' file should be './src/test/resources' folder of the "otc-test" project (applicable only for testing code - for production code scenario please refer to the last point below).

  2. When the "unittest.CompilerTest" class needs to read the './otc.yaml' file.
    This test class (explained under the "Unit Testing" topic below) which compiles the ".otcs" files and generates the source code, or any other test class in your project which does the same, will look for the './otc.yaml' file first in the classpath. Since this is a 'test class', this file needs to be in the classpath of the test classes, which means, this file should be under the 'test' folder hierarchy - i.e './src/test/resources' folder of the 'otc-test' maven project. And so, you need to copy this file from "${OTC_HOME}/config/" folder to the './src/test/resources' folder. Please verify if this behavior is the same in the build tool you are using as well. If the file is not found in the classpath, then it will look for it in "${OTC_HOME}/config/" folder.

    Note: Limit the dependence on "${OTC_HOME}" environment variable by the OTC Script Editor only. Dependence on it either by the test code or the production code is strongly discouraged. So please make sure the required file and folder is made available in the classpath as explained here in these points.

  3. When the "unittest.ExecutorTest" class needs to read both the './tmd' folder and the './otc.yaml' file.
    Same rule as mentioned in the above point on "unittest.CompilerTest" applies to this test class as well for both the './tmd' folder and the './otc.yaml' file.

  4. When the classes in production code of your project needs to read both the './tmd' folder and the './otc.yaml' file.
    The production code classes looks for this folder and the file in the classpath of the production code. And so, you should copy this file and the folder from under the 'test' folder hierarchy into the 'main' folder hierarchy - i.e './src/main/resources' folder of the 'otc-test' maven project.

OTC Script Files:

A few points about the OTCS files used as test cases in the sections.
  1. There are 58 test-case scenarios in the OTCS files located in the "${OTC_HOME}/otc-pretest" folder.
  2. The object-path mapping combinations of source and target in the sections were created to cover as many test scenarios as possible. So, the focus is not on what field mappings makes sense but rather the focus is on the mapping combinations scenarios only in the example test cases.
  3. The "${OTC_HOME}/otc-pretest" folder is like only a store for all the untested OTCS files.
  4. Whenever you want to unit-test a specific OTCS file, copy the file or the folder containing the OTCS file to "${OTC_HOME}/otc-unittest" folder.
  5. Once a OTCS file is unit-tested, the recommendation is to move them to the "${OTC_HOME}/otc-tested" folder. The reason to move them out after testing is to avoid repeated compilation of the already tested OTCS files on every test iteration.

Package structure:

To have the OTC framework generate the source code files to belong to any package structure, create the folder structure accordingly to match the package / namespace structure you desire.

The data objects:

JAXB objects created from WSDL/XSD are used as data-objects in the test cases. So some points to be noted on the DTOs used for testing are -
  1. The downloaded file contains a Maven project named "otcs-test-schema" which contains the DTOs referred to in all our examples and test-cases in these tutorials. The test-project has a dependency on this schema project. Import this project into your IDE and build it. Refer to these DTOs for a better understanding while doing the exercises.
  2. To facilitate demo of the OTC Script editor UI the compiled ".jar" file with these DTOs are already placed in "${OTC_HOME}/lib" folder.
  3. These are created from a schema related to airline travel domain.
  4. The schema contains all the required classes representing 2 different mock airlines named Kronos airlines and Athena airlines.
  5. These DTOs are annotated with JAXB components only for the purpose of generating the XML output in the examples to help in demonstration only. But, the OTC Framework does not depend on any such annotations.
  6. The original DTOs were updated with a few additional fields repeated in a few classes with the objective to cater to the different test scenarios only. So kindly ignore the sanity of such updates.
  7. To create .otcs files with the data-objects related to your project using the OTC Script Editor UI, package them into a ".jar" file and place it in the :"${OTC_HOME}/lib" folder. The OTC Script Editor looks for the types in this folder location.

Source Object XML:

Here is some information about the source object used in all the examples -
  1. The "./resources/test-samples/Kronos-passenger-map.xml" has the test-data. This file is picked either from the class path ("resources" folder) or from "${OTC_HOME}"
  2. The object KronosAirlinePassenger which is used as the source object in the almost all the test cases is created from this file.
  3. Having an understanding of this XML will help you to compare and analyze the output generated by the test-cases in the sections.
  4. While executing the examples given in the sections, please refer to this file to compare with the generated output.

Additional info:

Below are some additional important information.
  1. The last method parameter. All classes implementing the above interfaces and all OTCS generated classes takes a java.util.Map as the last method parameter. But when will you need this ?
    1. When you are integrating OTC in your software project, there may be scenarios wherein you may have some objects in your application to be made available within the generated code or within your custom helper or your custom converter (custom helpers and converters are explained in detail in later sections) for some additional processing logic which you want to add. In which case you may put such data in the map and then invoke the "otcExecutor.copyFromXXX(...)" method on the Executor passing this map as the last parameter.
    2. Note: Never add state (instance level objects) to the generated source code as using them in the code may cause undesired results. Instead, you can choose this option of passing the values in the map.

  2. There are 2 interfaces which you should know.
    1. The OTCS generated entry-class implements the "org.otcframework.common.executor.CodeExecutor" interface.
    2. Any custom converter you want to write should implement the "org.otcframework.common.converter.OtcConverter" interface.

Try it out:

In this exercise we will unit test the OTCS files which are provided in the "${OTC_HOME}/otc-pretest" folder.
  1. Start your file-manager and copy the "${OTC_HOME}/otc-pretest/cpyvalues1" folder to "${OTC_HOME}/otc-unittest" folder.
  2. This folder has a file named "com.athena.airlines.dto.AthenaAirlinePassenger.otcs".
  3. Set up the "otc-test" maven project in your favourite IDE.
  4. In the "otc-test" project, open the "./src/test/resources/otc.yaml" file and set the correct values for "compiler.paths.sourceCodeDirectory:" and "compiler.paths.tmdDirectory:" parameters pointing to locations within the test project.

    for example -
    compiler:
      paths:
        sourceCodeDirectory: D:/projects/otc-test/src/main/java
    
        tmdDirectory: D:/projects/otc-test/src/main/resources

    Note: Please refer to the 'Visibility aspects:' topic above..

  5. Open the "CompilerTest.java" file.
  6. Right click anywhere within this test class and select the Run or Debug option. The test method compiles the "*.otcs" file, generates and compiles the source-code.
  7. After this test method completes successfully, you may choose to review the generated object conversion source code. Please refer to the "unittest.CompilerTest" sub-topic above for the location details.
  8. Run "mvn install" from the terminal. This step will put the "./tmd" folder in the classpath.
  9. Next open the "ExecutorTest.java" file.
  10. In the "runCopyFromLiteralsTestCases()" method locate the line "String pkg = "...";".
  11. Ensure it is initialized to "cpyvalues1" as in "String pkg = "cpyvalues1";". This is the package name (folder which you just copied).
  12. If the OTCS file does not belong to any package then set this value to null;
  13. Run the test method - in your IDE (IntelliJ Idea or Eclipse) right click anywhere within the "runCopyFromLiteralsTestCases()" method and select the Run or Debug option.
  14. The test method automatically picks the generated code to be executed, executes it and marshals the created target object AthenaAirlinePassenger to XML / JSON and prints it on the console.
  15. Next try out the same steps to test another .otcs file.
  16. First delete the .otcs file ("/cpyvalues1" folder) under "${OTC_HOME}/otc-unittest" - we do not want the OTCS compiler to repeatedly spend time compiling the same ".otcs" files that was already compiled and repeatedly generate the source code.
  17. This time copy the "${OTC_HOME}/otc-pretest/cpysource_mixedpath" folder.
  18. Run the "CompilerTest" test class.
  19. In the other test method in the "ExecutorTest" test class, ie. "runCopyFromSourceObjectTestCases()", remember to change the value of 'pkg' variable to 'pkg = "cpysource_mixedpath;"'.
  20. Run the test method