Blitz Behavior

Operation During Execution

The typical process for executing a test file is to invoke Blitz from the command line and provide, as its single argument, the name of the root-level file. This causes Blitz to perform the following steps:

  1. Parse the file and build the test.
    1. The specified file is read into memory, validated, parsed, and converted into the appropriate kind of internal object (TestScenario, TestSuite, or TestCase, depending on the file’s extension).
    2. Attributes read from the file are stored on the test object, usually in the form of strings or collections of strings.
    3. As references to other test files (test cases or test suites only) are encountered, these are recursively read into memory and parsed.
    4. When a property tag is encountered in the XML file, each listed property and/or property file is loaded (in the sequence specified) and merged onto a common Properties sheet that is maintained on the current instance of the test being built.
    5. For a test case, inheritance details (specified by the inheritance tag) are maintained as strings; similarly, specified actual and expected result file names are stored. Commands, however, (specified by the command tag) are stored in a collection for later use.
    6. For a test suite or a test scenario, whether to run tests concurrently (specified by the sequential or concurrent tag) is stored as a boolean value, and the looping option (specified by the loopTimes or loopDuration tag) is stored as a locally declared type. However, for each occurrence of the testFile tag, the corresponding file is read and parsed recursively, and a corresponding test case or test suite is added to a collection of tests maintained on the current test object.
    7. For a test scenario, the additional details about error and failure limits is stored directly on the test object.
  2. Execute the test. Blitz hands off the fully constructed test object to an instance of blitz.concurrency.Executor to handle the actual execution of the test. The Executor's behavior here varies depending on the type of test object being executed:
    1. TestCase
      1. The Executor instantiates a special TestCaseDispatcher whose job is to communicate with the IOperative specified for the test case. If there are multiple test cases that are meant to run concurrently, then one such dispatcher is created for each test case.
      2. When the dispatchers are given the signal to start, each dispatcher invokes the prepareForExecution() method on its designated operative, and then it goes into a wait state pending notification that the other dispatchers have also completed. The invocation of this method includes:
        1. all inherited properties defined on the TestCase.
        2. the list of commands required for the TestCase.
        3. the names of the actual and expected result files (which may or may not actually be used by the IOperative).
      3. As soon as the last dispatcher signals completion, each dispatcher then invokes the execute() method on its designated operative, and again waits until notified that the operative is finished and that all dispatchers have finished. (Of course, if the test case is not running concurrently with other tests, the dispatcher does not have to wait after the operative signifies completion of each method.) The invocation of this method includes:
        1. an instance of the IMatcher that may be used by the IOperative to preprocess the actual and expected result files
        2. an instance of the IOutcome, to be used by the IOperative for recording the outcome of this particular test.
      4. After the dispatcher (or dispatchers, in the case of concurrently running tests) indicates to the Executor that it has completed, the Executor  then accumulates the result details for the test onto a special ResultCollector.
    2. TestSuite or TestScenario
      1. The Executor instantiates a special TestSuiteDispatcher whose job is to communicate with a TestSuiteHandler (a private implementation of the IOperative interface).
      2. The TestSuiteHandler creates a new instance of Executor and invokes it with each of the tests contained on the TestSuite or TestScenario. The TestSuiteHandler manages a ResultCollector of its own, and collects results for all of its nested TestCases. Any nested TestSuites are treated recursively and processing continues at b.) i.) above.
      3. As soon as the last nested TestCase has completed, the parent Executor combines the results from the TestSuite’s ResultCollector onto its own ResultCollector.
  3. Display the results. Blitz completes execution by displaying a summary of the results on the console. This may include a message about terminating early due to excessive failures, or it may include exception messages resulting from parsing, validation or other I/O errors. Assuming that termination did not result in an exception, then Blitz also creates a file containing the details of the results.

TOP

How Inheritance Works

Every test case must be able to execute within its own well-defined environment. From one test case to the next, however, there may be many characteristics of an environment that are common to them all. It is convenient to be able to specify these common characteristics at a higher level so that individual test cases can easily share them. Blitz provides several inheritance models — property inheritance, matcher inheritance, agency inheritance, and path inheritance — each of which is geared to a particular attribute that a test case is likely to need.

  1. Property Inheritance tells Blitz how to assemble properties for a single test case before the test case is executed. Three modes are provided:
    • NONE: Tells Blitz to use only those properties which are explicitly declared on the test case itself; if the test case has no such declarations, then no properties are defined at all.
    • PARENT: Tells Blitz to construct a Properties sheet by getting the properties from the immediate parent of this test case (if it has a parent, which must be a test suite or test scenario), and then overriding the resulting sheet with properties that are explicitly declared on the test case itself.
    • ROOT: This is very similar to PARENT inheritance, except that the construction of the Properties sheet begins at the highest-level test — i.e. the "root" test that was supplied as an argument to Blitz at start up. Then, Blitz works its way down the hierarchy through each more recent ancestor (overriding properties along the way), and finishes by overriding the result with properties that are explicitly declared on the test case itself.
  2. Matcher Inheritance determines where Blitz will look for a matcher that will be used by the test case for comparing actual result output to expected result output. Three modes are provided:
    • NONE: Tells Blitz to use the matcher that is explicitly defined on the test case itself; if no matcher is specified, then Blitz uses the matcher class defined in the system properties, or a default NullMatcher if no such property is defined..
    • PARENT: Tells Blitz to use the matcher that is explicitly defined on the test case itself; if no matcher is specified on the test case, then Blitz looks to the immediate parent for a specification; if no specification can be found, then the default matcher is used instead.
    • ROOT: This is very similar to PARENT inheritance, except that Blitz continues searching up the ancestral chain for the first specification of a matcher. If no specification can be found, then the default matcher is used instead.
  3. Agency Inheritance is used implicitly by Blitz to determine which agency (factory class) to use for a given test. If the current test has no agency defined, then Blitz searches upward along the ancestral chain until a specification can be found; if none can be found, Blitz looks in the system properties file for a specification, or, failing that, generates an error message since no further processing can be done.
  4. Path Inheritance provides a way for Blitz to locate property files and test files (as well as expected and actual result files). This form of inheritance is implicit, and does not require any specific declarations in any XML test files.

    In the course of specifying a test, you can include either or both of the tags testFileRootPath and propertyFileRootPath. Blitz uses these path declarations only when the relevant filenames are not already absolute. A filename reference is considered absolute if it contains sufficient information to locate the file within the file-system without any external assumptions. So, for example, any Windows-based filename that contains a drive letter would, by definition, be absolute. By default, any filename that is not already absolute must be relative, which means that the operating system must locate that file relative to a particular path; if no such path is defined anywhere, then the current working directory (represented by a single dot (".")) is assumed.
    • testFileRootPath: The specification of this path tells Blitz the base location of any test files, actual result files, and expected result files that are specified in relative mode. At run-time, when Blitz tries to load any of these types of files, it checks to see if the reference is absolute; if it is not, then Blitz constructs an absolute reference by concatenating this path with the given filename. If the test itself does not define such a path, then Blitz searches upward along the ancestral chain until it finds a path definition; if no definition is found, then Blitz looks in the system properties file for a definition, or, failing that, uses the path in which the root test was located.
    • propertyFileRootPath: The specification of this path tells Blitz the base location of any property files that are specified in relative mode. The inheritance behavior of Blitz is the same as for testFileRootPath above.

NOTE: Filter Inheritance does not exist. When a matcher for a test is identified, only the filter directly associated with that particular matcher is actually used; if no filter is defined for the matcher, then a NullFilter is used instead.

TOP