Join 34,000+ subscribers and receive articles from our blog about software quality, testing, QA and security.
 

[Migration] TestRail to Testlink


#1

We have a customer working with Testlink, who wanted to have parts of our test suite. So I wrote an XSLTransformation to convert XML exports of a testsuite to Testlink’s XML format.

I used Saxon for the actual transformation:

Transform -s:testrail.xml -xsl:t2t.xslt -o:testlink.xml

Hope it helps someone :slight_smile:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output
        method="xml"
        encoding="UTF-8"
        indent="yes"
        cdata-section-elements="details externalid version summary preconditions execution_type importance name value step_number actions expectedresults execution_type"
        />

    <!-- Process the root node. Create top "testsuite" and 
         process "sections" nodes. -->
    <xsl:template match="/">
        <testsuite>
            <xsl:attribute name="name">
                <xsl:value-of select="name" />
            </xsl:attribute>
            <details>
                <xsl:value-of select="description"/>
            </details>
            <xsl:apply-templates />
        </testsuite>
    </xsl:template>

    <!-- Process all "section" nodes. Create a "testsuite" node for each and
         process sub"sections" recursively or "cases" nodes -->
    <xsl:template match="sections">
        <xsl:for-each select="section">
            <testsuite>
                <xsl:attribute name="name">
                    <xsl:value-of select="name" /> 
                </xsl:attribute>
                <xsl:apply-templates />
            </testsuite>
        </xsl:for-each>
    </xsl:template>

    <!-- Process all "cases" nodes. Process a "testcase" node for each
         sub"case" node. -->
    <xsl:template match="cases">
        <xsl:for-each select="case">
            <xsl:call-template name="testcase" />
        </xsl:for-each>
    </xsl:template>

    <!-- Process a "case" node. Create a "testcase" node and
         process all "steps" nodes inside.
         Create custom fields as necessary.  -->
    <xsl:template name="testcase">
        <testcase>
            <xsl:attribute name="name">
                <xsl:value-of select="title" />
            </xsl:attribute>
            <node_order></node_order>
            <externalid>
                <xsl:value-of select="substring(id,2)"/>
            </externalid>
            <version></version>
            <summary></summary>
            <preconditions>
                <!-- Remove references to images. We can't export these. -->
                <xsl:value-of select="replace(custom/preconds, '!\[\](.*)', '')" />
            </preconditions>
            <execution_type>
                1<!-- 1: manual, 2: automatic -->
            </execution_type>
            <importance>
                <xsl:if test="priority = '1 - Must Test'">3</xsl:if>
                <xsl:if test="priority = '2 - Test If Time'">2</xsl:if>
            </importance>
            <estimated_exec_duration><xsl:value-of select="estimate" /></estimated_exec_duration>
            <status></status>
            <!-- Process steps -->
            <xsl:apply-templates />
            <custom_fields>
                <custom_field>
                    <name>milestone</name>
                    <value>
                        <xsl:value-of select="milestone" />
                    </value>
                </custom_field>
                <custom_field>
                    <name>tester_role</name>
                    <value>
                        <xsl:value-of select="substring(custom/bvg_precond_used_rolle, 3)" />
                    </value>
                </custom_field>
                <custom_field>
                    <name>reference</name>
                    <value>
                        <xsl:value-of select="references" /></value>
                </custom_field>
            </custom_fields>
        </testcase>
    </xsl:template>

    <!-- Processing of the steps of a testcase -->
    <xsl:template match="steps_separated">
        <steps>
            <xsl:for-each select="step">
                <step>
                    <step_number>
                        <xsl:value-of select="index" />
                    </step_number> 
                    <actions>
                        <!-- Remove references to images. We can't export these. -->
                        <xsl:value-of select="replace(content, '!\[\](.*)', '')" />
                    </actions>
                    <expectedresults>
                        <!-- Remove references to images. We can't export these. -->
                        <xsl:value-of select="replace(expected, '!\[\](.*)', '')" />
                    </expectedresults>
                    <execution_type>1</execution_type>
                </step>
            </xsl:for-each>
        </steps>
    </xsl:template>

    <!-- Redefine default template, so nodes are ignored that are not matched by the templates above.
         (Otherwise, the default template would just print their value.) -->
    <xsl:template match="text()|@*" />
</xsl:stylesheet>

#2

Hello,

Thanks a lot for posting this! We have customers in similar situations from time to time (i.e. sharing test cases with customers or vendors who have not yet switched to TestRail :smiley: ) who will certainly find this useful.

It’s appreciated!


#3

Hello everyone!

Because I’ve encountered this situation myself, a colleague and myself have worked on the script to also transfer steps.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output
        method="xml"
        encoding="UTF-8"
        indent="yes"
        cdata-section-elements="details version summary preconditions execution_type importance name value step_number actions expectedresults execution_type"
        />

    <!-- Process the root node. Create top "testsuite" and 
         process "sections" nodes. -->
    <xsl:template match="/">
        <testsuite>
            <xsl:attribute name="name">
                <xsl:value-of select="name" />
            </xsl:attribute>
            <details>
                <xsl:value-of select="description"/>
            </details>
            <xsl:apply-templates />
        </testsuite>
    </xsl:template>

    <!-- Process all "section" nodes. Create a "testsuite" node for each and
         process sub"sections" recursively or "cases" nodes -->
    <xsl:template match="sections">
        <xsl:for-each select="section">
            <testsuite>
                <xsl:attribute name="name">
                    <xsl:value-of select="name" /> 
                </xsl:attribute>
                <xsl:apply-templates />
            </testsuite>
        </xsl:for-each>
    </xsl:template>

    <!-- Process all "cases" nodes. Process a "testcase" node for each
         sub"case" node. -->
    <xsl:template match="cases">
        <xsl:for-each select="case">
            <xsl:call-template name="testcase" />
        </xsl:for-each>
    </xsl:template>

    <!-- Process a "case" node. Create a "testcase" node and
         process all "steps" nodes inside.
         Create custom fields as necessary.  -->
    <xsl:template name="testcase">
        <testcase>
            <xsl:attribute name="name">
                <xsl:value-of select="title" />
            </xsl:attribute>
            <node_order></node_order>
            <version></version>
            <summary></summary>
            <preconditions>
                <!-- Remove references to images. We can't export these. -->
                <xsl:value-of select="replace(custom/preconds, '!\[\](.*)', '')" />
            </preconditions>
            <execution_type>
                1<!-- 1: manual, 2: automatic -->
            </execution_type>
            <importance>
                <xsl:if test="priority = '1 - Must Test'">3</xsl:if>
                <xsl:if test="priority = '2 - Test If Time'">2</xsl:if>
            </importance>
            <estimated_exec_duration><xsl:value-of select="estimate" /></estimated_exec_duration>
            <status></status>
            <!-- Process steps -->
            <xsl:apply-templates />
            <custom_fields>
                <custom_field>
                    <name>milestone</name>
                    <value>
                        <xsl:value-of select="milestone" />
                    </value>
                </custom_field>
                <custom_field>
                    <name>tester_role</name>
                    <value>
                        <xsl:value-of select="substring(custom/bvg_precond_used_rolle, 3)" />
                    </value>
                </custom_field>
                <custom_field>
                    <name>reference</name>
                    <value>
                        <xsl:value-of select="references" /></value>
                </custom_field>
            </custom_fields>
        </testcase>
    </xsl:template>

    <!-- Processing of the steps of a testcase -->
    <xsl:template match="teststeps">
        <steps>
            <xsl:for-each select="step">
                <step>
                    <step_number>
                        <xsl:value-of select="index" />
                    </step_number> 
                    <actions>
                        <!-- Remove references to images. We can't export these. -->
                        <xsl:value-of select="replace(content, '!\[\](.*)', '')" />
                    </actions>
                    <expectedresults>
                        <!-- Remove references to images. We can't export these. -->
                        <xsl:value-of select="replace(expected, '!\[\](.*)', '')" />
                    </expectedresults>
                    <execution_type>1</execution_type>
                </step>
            </xsl:for-each>
        </steps>
    </xsl:template>

    <!-- Redefine default template, so nodes are ignored that are not matched by the templates above.
         (Otherwise, the default template would just print their value.) -->
    <xsl:template match="text()|@*" />
</xsl:stylesheet>

This worked for me, so enjoy!


#4

Hi,

Thanks for sharing your script. Appreciate your effort.
I have tried with your script but I am not able to import ‘Steps’ and ‘Expected Results’.
Could you please help on the same.

Regards,