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:
- Parse the file and build the test.
- 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).
- Attributes read from the file are stored on the test object, usually
in the form of strings or collections of strings.
- As references to other test files (test cases or test suites only)
are encountered, these are recursively read into memory and parsed.
- 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.
- 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.
- 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.
- For a test scenario, the additional details about error and failure
limits is stored directly on the test object.
- 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:
TestCase
- 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.
- 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:
- all inherited properties defined on the
TestCase .
- the list of commands required for the
TestCase .
- the names of the actual and expected result files (which may or
may not actually be used by the
IOperative ).
- 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:
- an instance of the
IMatcher that may be used by the
IOperative to preprocess the actual and expected result
files
- an instance of the
IOutcome , to be used by the
IOperative for recording the outcome of this particular test.
- 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 .
TestSuite or TestScenario
- The
Executor instantiates a special TestSuiteDispatcher whose job is to
communicate with a TestSuiteHandler (a private implementation of the
IOperative interface).
- 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.
- As soon as the last nested
TestCase has completed,
the parent Executor combines
the results from the TestSuite ’s ResultCollector onto its own
ResultCollector.
- 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.
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.
- 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.
- 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.
- 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.
- 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
|