3
\$\begingroup\$

I am attempting to develop an XML schema (XSD) to represent a set of unit tests for some code. (Specifically, I am testing Oracle PL/SQL stored procedures.)

I would like to have something that represents:

  • the procedure itself
  • the data used for each test (both arguments to be supplied to the procedure, and data to be loaded into the database before beginning the test(s))
  • the expected result of each test

Each procedure can have multiple tests, but I think I would like to only have the tests for one procedure in each XML file.

Below is the schema I have so far: any comments or suggestions would be welcomed!

<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.example.com/TestData"
    elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://www.example.com/TestData">
    <complexType name="ProcedureType">
        <complexContent>
            <extension base="tns:AbstractProcedureType">
            </extension>
        </complexContent>
    </complexType>
    <complexType name="FunctionType">
        <complexContent>
            <extension base="tns:AbstractProcedureType">
                <attribute name="ReturnType" type="tns:DataTypeType"></attribute>
            </extension>
        </complexContent>
    </complexType>
    <complexType name="AbstractProcedureType" abstract="true">
        <sequence>
            <element name="PackageName" type="string" minOccurs="0" maxOccurs="1"></element>
            <element name="Name" type="string" minOccurs="1" maxOccurs="1"/>
            <element name="Arguments" type="tns:ArgumentsType"
                minOccurs="1" maxOccurs="1" />
        </sequence>
    </complexType>
    <complexType name="ArgumentType">
        <attribute name="Name" type="string" use="required"/>
        <attribute name="DataType" type="tns:DataTypeType"
            use="required"/>
    </complexType>
    <simpleType name="DataTypeType">
        <restriction base="string">
            <enumeration value="VARCHAR2"/>
            <enumeration value="NUMBER"/>
        </restriction>
    </simpleType>
    <complexType name="TestType">
        <sequence>
            <choice maxOccurs="1" minOccurs="1">
                <element name="Procedure" type="tns:ProcedureType"></element>
                <element name="Function" type="tns:FunctionType"></element>
            </choice>

            <element name="Instance" type="tns:TestInstanceType"></element>
        </sequence>
    </complexType>
    <complexType name="TestInstanceType">
        <sequence>
            <element name="Arguments" type="tns:ArgumentType"
                maxOccurs="unbounded" minOccurs="0">
            </element>
            <element name="ExpectedResult"
                type="tns:ExpectedResultType" maxOccurs="1" minOccurs="1">
            </element>
        </sequence>
    </complexType>
    <complexType name="ExpectedResultType">
        <attribute name="Type" use="required">
            <simpleType>
                <restriction base="string">
                    <enumeration value="value"></enumeration>
                    <enumeration value="exception"></enumeration>
                    <enumeration value="null"></enumeration>
                    <enumeration value="nothing"></enumeration>
                    <enumeration value="query"></enumeration>
                </restriction>
            </simpleType>
        </attribute>
    </complexType>
    <complexType name="ArgumentsType">
        <sequence>
            <element name="Argument" type="tns:ArgumentType" maxOccurs="1" minOccurs="0"></element>
        </sequence>
    </complexType>
    <element name="Tests" type="tns:TestsType"></element>
    <complexType name="TestsType">
        <sequence>
            <element name="Test" type="tns:TestType" maxOccurs="unbounded" minOccurs="0"></element>
        </sequence>
    </complexType>
</schema>
\$\endgroup\$
2
\$\begingroup\$
  • I would move PackageName and Name elements of the AbstractProcedureType to attributes.
  • I don't think you need an AbstractProcedureType, just have a ProcedureType and FunctionType derived from it.
  • it's not clear why ArgumentsType may have either 0 or 1 argument... I would say that it should have maxOccurs="unbounded"
  • I would make Arguments element optional in ProcedureType and FunctionType (minOccurs="0"), and require at least one argument if the node is present.

Now let's move to global changes...

  • Since you want to have a single procedure/function per XML we can move the declaration of procedure right under the root node.
  • I removed some of the named complextypes in favor of in-place type definitions
  • You can add some referential constraints to XSD (e.g. argument names in test should match procedure/function declaration).

As a result of these changes I've got the following XSD:

<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.example.com/TestData"
        elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
        xmlns:tns="http://www.example.com/TestData">
    <element name="Tests" type="tns:TestsType">
        <key name="Argument.Name">
            <selector xpath="tns:Procedure/tns:Arguments/tns:Argument | tns:Function/tns:Arguments/tns:Argument" />
            <field xpath="@Name" />
        </key>
    </element>
    <complexType name="TestsType">
        <sequence>
            <choice maxOccurs="1" minOccurs="1">
                <element name="Procedure" type="tns:ProcedureType" />
                <element name="Function" type="tns:FunctionType" />
            </choice>
            <element name="Test" type="tns:TestInstanceType" minOccurs="0" maxOccurs="unbounded" />
        </sequence>
    </complexType>
    <complexType name="ProcedureType">
        <sequence>
            <element name="Arguments" minOccurs="0" maxOccurs="1">
                <complexType>
                    <sequence>
                        <element name="Argument" minOccurs="0" maxOccurs="unbounded">
                            <complexType>
                                <attribute name="Name" type="string" use="required" />
                                <attribute name="DataType" type="tns:DataTypeType" use="required" />
                            </complexType>
                        </element>
                    </sequence>
                </complexType>
            </element>
        </sequence>
        <attribute name="PackageName" type="string" use="optional" />
        <attribute name="Name" type="string" use="required" />
    </complexType>
    <complexType name="FunctionType">
        <complexContent>
            <extension base="tns:ProcedureType">
                <attribute name="ReturnType" type="tns:DataTypeType" use="required" />
            </extension>
        </complexContent>
    </complexType>
    <simpleType name="DataTypeType">
        <restriction base="string">
            <enumeration value="VARCHAR2" />
            <enumeration value="NUMBER" />
        </restriction>
    </simpleType>
    <complexType name="TestInstanceType">
        <sequence>
            <element name="Argument" minOccurs="0" maxOccurs="unbounded">
                <complexType>
                    <attribute name="Name" type="string" use="required" />
                    <attribute name="Value" type="string" use="required" />
                </complexType>
                <keyref name="Argument.Name.Exists" refer="tns:Argument.Name">
                    <selector xpath="." />
                    <field xpath="@Name" />
                </keyref>
            </element>
            <element name="ExpectedResult" type="tns:ExpectedResultType" maxOccurs="1" minOccurs="1" />
        </sequence>
    </complexType>
    <complexType name="ExpectedResultType">
        <attribute name="Type" use="required">
            <simpleType>
                <restriction base="string">
                    <enumeration value="value" />
                    <enumeration value="exception" />
                    <enumeration value="null" />
                    <enumeration value="nothing" />
                    <enumeration value="query" />
                </restriction>
            </simpleType>
        </attribute>
    </complexType>
</schema>

What I would do next:

  • Create several elements for different expected result types (query, exception, etc) and define corresponding attributes and elements on them
  • You can also replace generic Argument with typed elements, that will allow you to define even more constraints on values of arguments in tests
\$\endgroup\$
  • \$\begingroup\$ Excellent - thanks so much for your help! \$\endgroup\$ – Allan Lewis May 22 '13 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.