user98208
user98208

Reputation: 43

How to send results from TestComplete directly to TeamCity

A previous post on this subject has been helpful (see How to export TestExecute/TestComplete results to teamcity?)

Right now, our TestComplete solution creates a text file that MSBuild consumes with a Microsoft.Build.Utilities.Task helper that sends the messages back up to TeamCity. However, I'm wondering if we can do better.

The TeamCity help topic (http://confluence.jetbrains.com/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages) says, messages "should be printed into standard output stream of the build". What I'm trying to figure out is, can I directly access that stream from the TestComplete script?

I was hoping that it would be something as simple as: dotNET.System.Console.WriteLine_11("##teamCity[testSuiteStarted name='Foo']");

But that is obviously not giving me the correct output stream. Thoughts? Can it be done?

Upvotes: 2

Views: 2250

Answers (3)

aydjay
aydjay

Reputation: 868

If you are using TestExecute this has become "Relatively" simple recently

Execute TestExecute with the following parameters:

/exportlog:"log\Index.html" /SilentMode /DoNotShowLog

For a full rundown of TestExecute parameters check: https://support.smartbear.com/testexecute/docs/running/automating/command-line/command-line.html

Setup your TestComplete project with a build level report:

Location of report tab on the project page

Please be aware it is case sensitive

On your build configuration, in the general settings tab, export the generated log folder as an artifact. enter image description here

Once the build has run, you will be able to see the test log embedded within TeamCity:

Sample embedded project

This is using TestComplete V14 and TeamCity 2019.1

Upvotes: 0

nicholas
nicholas

Reputation: 3047

The issue you are experiencing is that TestComplete does not support writing to the standard output stream, even if you invoke the CLR Console.WriteLine, you are writing to the process named tcHostingProcess.exe in which all CLR objects exist.

In order to work around this, you need a console application running that can accept messages from your TestComplete project. There are a number of ways to do this, but here is a proposed solution using TCP/IP for the necessary IPC.

In TeamCity

In the TeamCity build steps, you want the TestComplete or TestExecute process to launch without holding up the build script and then to launch a custom console application which will receive the messages from TestComplete.

start TestComplete.exe [arg1] [arg2] ...
TCConsoleHost.exe

Console Host Application

The console host program will start a TcpListener, and once a client connects, it will read messages from the resulting NetworkStream object and print them to the console. This program will continue until there is an error in reading from the stream (i.e. TestComplete has exited).

class Program
{
    static void Main(string[] args)
    {
        TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9800);
        listener.Start();
        TcpClient client = listener.AcceptTcpClient();
        NetworkStream stream = client.GetStream();
        BinaryReader reader = new BinaryReader(stream);

        while (true)
        {
            try
            {
                string message = reader.ReadString();
                Console.WriteLine(message);
            }
            catch
            {
                break;
            }
        }
    }
}

Message Client Class

Likewise, we can create a TcpClient which can connect to our listening process and relay messages. The real trick here is to wrap this in a static class with a static constructor so that once TestComplete loads the CLR bridge, it will automatically connect and be ready to send the messages. This example has a TeamCity service message function SendMessage which automatically formats the message (including escaping single quotes).

public static class TCServiceMessageClient
{
    static BinaryWriter writer;
    static NetworkStream stream;

    static TCServiceMessageClient()
    {
        TcpClient client = new TcpClient();
        client.Connect("127.0.0.1", 9800);

        stream = client.GetStream();
        writer = new BinaryWriter(stream);
    }

    public static void SendMessage(string message)
    {
        writer.Write(string.Format("##teamcity[message text='{0}'", message.Replace("'","|'")));
    }
}

In TestComplete

Now, since the client will automatically connect when the CLR bridge is loaded, the only code necessary in TestComplete is:

dotNET["TCServiceMessageClient"]["TCServiceMessageClient"]["SendMessage"]("Hello TeamCity!");

Additional Notes

There are a few caveats with the above code, namely timing, reconnect, etc. A deployed solution should have better error handling of the network conditions. Additionally, it may be more desirable that the TCMessageHost application actually launches TestComplete directly with a System.Process object so that it can more reliably wait for the application to exit.

Upvotes: 1

AStouder
AStouder

Reputation: 75

I'm working with TeamCity and TestComplete as well. I ran into the exact same problem, and you can't write to the console no matter what you try to do, at least not that I've found. I even tried writing a C# app that I used the CLR bridge in TestComplete to talk to it, and still nothing.

What we ended up doing was to create batch file that handles the TestExecute call. We then call the batch file from TeamCity. To call TestExecute use this (with your pjs file and /p specific to your tests):

start /wait TestExecute "{TestCaseID}.pjs" /r /e /p:"{TestCaseID}" /silentmode /forceconversion

Then we check the ERRORLEVEL property returned by TestExecute. A level of 2 means that the test execution failed. In that case, we print the following with an echo statement:

##teamcity[testFailed name='{TestCaseID} - {TestName}' message='TestExecute reported a failure' details='See artifacts']

After TestExecute closes, it will attempt to open the log file. We have our build servers set so that mht files are opened by the calculator since they take a while to open (set it as the default in the Windows Right-Click -> Open With menu). So, after TestExecute completes, it opens the calculator, then our batch file just kills the calculator using taskkill.

We then copy the result file from TestExecute into a specified location (this is a separate executable that we wrote). The we finish the test execution and publish our artifacts to TestExecute:

echo ##teamcity[testFinished name='{TestCaseID} - {TestName}' duration='%milliSecondsElapsed%']
echo ##teamcity[publishArtifacts 'C:\BuildResults\{GroupName}\{TestCaseID}\*.mht']

One thing to note: We were never able to get TestExecute to reliably run a single test at a time, so we actually use a list of our test cases to generate separate pjs and mds files for each of our test cases. Then we use those generated pjs and mds files in TestExecute. That's where the {GroupName}, {TestCaseID}, and {TestName} variables from above come from. Those are specific to our implementation, but you may need different indicators there.

Hope this helps!

Upvotes: 0

Related Questions