Integrate xUnit.net with Universal Agent
In this article we will walk you through an example to integrate, execute and schedule Test Execution for your xUnit.net test project with Universal Agent.
This article will integrate xUnitSample project with Universal Agent.
Notes
The test project used in this article is stored on github at https://github.com/Tricentis-qTest/launch-samples/ containing:
-
Dotnet core test projects that target .NET.Core 2.2+: xUnitSample, MSTest sample, NUnit sample
-
Dotnet project that targets .NET.4.5 (Windows only): UnitTestSample
Prerequisites
-
You machine needs to have .NET Core 2.2+ installed.
-
This article uses Selenium that opens Chrome browser, so make sure you also install Google Chrome to the machine where Automation Host is running.
Step 1: Create a new Universal Agent for your xUnit test project
Access to your Automation Host UI. Click the Add button to create a new agent. The New Agent dialog will be shown.
Next, enter the following to the New Agent dialog:
General Information
-
Agent Name: Enter name of the agent, such as xUnit Agent
-
qTest Manager Project: Select a qTest Manager project from which the agent is going to execute scheduled tests, such as qConnect Sample Project
-
Agent Type: Select Universal Agent
Pre-Execute Script
We are going to configure a pre-execute script to either clone the sample code from github or pull the latest updates of that repo if it already exists to the host machine.
Enter the script below to the Pre-Execute Script editor, which is specific to the Operating System that the Automation Host is running:
Windows
if not exist "C:\dotnet-samples" (
cd /d C:\
git clone https://github.com/QASymphony/dotnet-samples
) else (
cd /d "C:\dotnet-samples"
git pull --all
)
Linux or Mac
#!/bin/bash
if [ ! -d "/usr/local/var/dotnet-samples" ] then
cd "/usr/local/var"
git clone https://github.com/QASymphony/dotnet-samples
else
cd /usr/local/var/dotnet-samples
git pull –all
fi
Execute Command
Executor
Select node as the script executor
Working Directory:
-
If your host machine is running on Linux or Mac, enter /usr/local/var/dotnet-samples
-
If your host machine is running on Windows, enter C:\dotnet-samples
Execute Command
We are going to create a script written in Node.js to:
-
Obtain the automation content(s) of scheduled test runs by resolving the value of magic variable TESTCASES_AC. Refer to Using Magic Variables in Universal Agent to learn more about magic variables in Universal Agent.
-
If the value of TESTCASES_AC variable is empty, which means there is no test runs being scheduled to be executed (such as when this Universal Agent is executed in the first time), we will let the agent execute the whole tests in our xUnitSample project then create test run logs to qTest Manager as the execution result.
-
If TESTCASES_AC variable has value, which are the automation contents of scheduled test runs, we will instruct Universal Agent to only execute tests that match those automation contents.
Enter the code snippet below to the Execute Command editor.
// sample Execute Command for executing .NET core test with node executor
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
let isWin = process.platform == "win32";
// process.env.WORKING_DIR holds the value of Working Directory configured in Universal Agent,
// if you did not specify Working Directory in Universal Agent, make sure you enter it here
let SOLUTION_DIR = process.env.WORKING_DIR || ``;
// validate the existence of SOLUTION_DIR, it it does not exist, print error and stop execution
if (!fs.existsSync(SOLUTION_DIR)) {
console.error('No working directory found.');
return;
}
// change .NET core version to fit your need
let TARGET_DOTNETCORE_VERSION = '2.2';
// path to test project, change it to reflect yours
let TEST_PROJECT_NAME = 'xUnitSample';
let TEST_PROJECT_DIR = path.resolve(SOLUTION_DIR, 'DotnetCore', TEST_PROJECT_NAME);
let TEST_PROJECT_PATH = path.resolve(TEST_PROJECT_DIR, `${TEST_PROJECT_NAME}.csproj`);
// possible value for configuration: Debug or Release
let CONFIGURATION = 'Debug';
// we are going to execute published test
let PUBLISH_DIR = path.resolve(TEST_PROJECT_DIR, 'bin', CONFIGURATION, `netcoreapp${TARGET_DOTNETCORE_VERSION}`, 'publish');
// path to the test project output
let TEST_PROJECT_PUBLISHED_PATH = path.resolve(PUBLISH_DIR, `${TEST_PROJECT_NAME}.dll`);
// this is the full path to dotnet command
// make sure you change it to reflect your environment
let DOTNET_EXECUTABLE_PATH = isWin ? 'C:/Program Files/dotnet/dotnet.exe' : '/usr/local/bin/dotnet';
// by default, the result folder will be created at ${SOLUTION_DIR}/TestResults
let RESULT_DIR = path.resolve(`${SOLUTION_DIR}`, 'TestResults');
// this is the path to XML test result
let PATH_TO_XML_RESULT = path.resolve(`${RESULT_DIR}`, 'Results.xml');
// delete result dir if it exists
if (fs.existsSync(RESULT_DIR)) {
if (isWin) {
execSync(`rmdir /s /q "${RESULT_DIR}"`);
} else {
execSync(`rm -rf "${RESULT_DIR}"`);
}
}
// the shell command builder
var commandBuilder = [];
if (!isWin) {
// set DOTNET_CLI_HOME environment var to working directory for dotnet command to work properly
commandBuilder.push(`export DOTNET_CLI_HOME="${SOLUTION_DIR}"`);
}
// execute `dotnet publish` command to publish the test project,
// the published results will be stored in PUBLISH_DIR
commandBuilder.push(`"${DOTNET_EXECUTABLE_PATH}" publish "${TEST_PROJECT_PATH}"`);
// copy the chromedriver to the PUBLISH_DIR dir for the Selenium test to run properly
// remove these two commands if you're not running Selenium tests and so there will be no chromedriver in the output
let CHROME_DRIVER_NAME = isWin ? 'chromedriver.exe' : 'chromedriver';
let COPY_COMMAND = isWin ? 'copy' : 'cp';
let PATH_TO_CHROME_DRIVER = path.resolve(TEST_PROJECT_DIR, 'bin', CONFIGURATION, `netcoreapp${TARGET_DOTNETCORE_VERSION}`, CHROME_DRIVER_NAME);
commandBuilder.push(`${COPY_COMMAND} "${PATH_TO_CHROME_DRIVER}" "${PUBLISH_DIR}"`);
/**
* Kicks off the test. What it does is to resolve the value of TESTCASES_AC variable and validate:
* Case 1: if that variable TESTCASES_AC has value, meaning there is/are test run(s) being scheduled in qTest Manager
* -- for each test run being scheduled, finds and executes test method whose name matches that test run's automation content
* Case 2: the value of TESTCASES_AC is empty, meaning no test runs being scheduled when the Universal Agent is executed in the first time
* -- executes all the tests within the project output DLL
*/
let testMethods = ($TESTCASES_AC && $TESTCASES_AC.trim() != '') ? `/Tests:${TESTCASES_AC}` : ``;
if (testMethods != '') {
// run specific test methods, change the log file path if possible
commandBuilder.push(`"${DOTNET_EXECUTABLE_PATH}" vstest "${TEST_PROJECT_PUBLISHED_PATH}" ${testMethods} --logger:"nunit;LogFilePath=${PATH_TO_XML_RESULT}"`);
} else {
// run all tests, change the log file path if possible
commandBuilder.push(`"${DOTNET_EXECUTABLE_PATH}" vstest "${TEST_PROJECT_PUBLISHED_PATH}" --logger:"nunit;LogFilePath=${PATH_TO_XML_RESULT}"`);
}
// the dotnet test runner will throw exception when there is an assertion failed and causes the
// test exited with code 1. So we wrap the command execution in a try catch block to ensure the
// execute command is fully executed and not exited with code 1 when there is exception thrown from the test
try {
// build the shell command
let command = isWin ? commandBuilder.join(' && ') : commandBuilder.join('\n');
// execute the shell command
execSync(command, { stdio: "inherit" });
} catch (err) {
console.error(`*** Test execution error ***`);
console.error(err.message || err);
console.error(`*** End test execution error ***`);
}
Path to Results
Enter the value below to specify the path to the test results generated when the execution completes.
-
If your host machine is running on Linux or Mac: /usr/local/var/dotnet-samples/TestResults
-
If your host machine is running on Windows: C:\dotnet-samples\TestResults
Result Parser
Select NUnit as the Result Parser.
If you do not find the NUnit parser in the Result Parser list, follow below steps to make it available to Universal Agent.
-
Temporarily save the xNUnit Agent. You'll be return back to the Automation Host dashboard.
-
Download NUnit parser here.
-
Access qTest Launch and refer to Upload the Parser to qTest Launch to upload the Parser to qTest Launch.
-
Go back to Automation Host UI, refresh the page for it to load the new parsers.
-
Select to edit xUnit Agent and you'll find the NUnit parser from the Result Parser list.
Select Save to finish creating the agent. Below is how xUnit Agent looks like, on Mac.
Step 2: Execute the Agent
From Agents list, locate the xUnit Agent. Then click the Action icon
for the Agent. Then select Run now.
The agent execution dialog will display.
Click the Execute button to execute the agent. Once the execution completes, the Console Log shows all the test results have been submitted to qTest Manager, as highlighted in the screenshot below.
Next, access qTest Manager. Select qConnect Sample Project then go to the Test Execution module. You'll see the test results submitted to qTest Manager as Test Runs under a Test Suite named Automation YYYY-MM-DD, where YYYY-MM-DD is the date the agent is executed, as below.
Now we are going to schedule Test Execution for some selected test runs with the xUnit Agent and verify it only executes tests that match the scheduled test runs as well submits results to those test runs.
Step 3: Schedule Test Execution for specific Test Runs
From qTest Manager, select the project qConnect Sample Project, then click the Test Execution tab and do the following:
-
Select the newly created test suite. In our example, it is Automation 2019-01-14.
-
From the test run list on the right, select the first four test runs.
-
Click the More button, then select Schedule.
On the Schedule Automation Test Execution dialog, enter the following:
-
Name: Name of the schedule, such as Execute xUnit Test with Universal Agent
-
Agent: Select the xUnit Agent
-
Click the OK button to complete Test Execution scheduling for the selected three test runs
Now go back to the Automation Host UI. Click the Poll Now button.
At this stage, the Automation Host does the following:
-
Immediately polls to qTest Manager to load schedule jobs
-
Executes the job execution for the tests methods in xUnitSample project that match the automation content of the four scheduled test runs
Wait for a while for the execution completes, then click the Action icon
for the Agent. Then select View Log.
A log screen will display that shows the latest Execution logs. Verify that the log reported only four tests being executed.
Now get back to Test Execution module of qConnect Sample Project project in qTest Manager. Click on each test run that was scheduled to be executed, you'll see the Execution History of each test run was updated with two test run logs: one test log created when the agent was first executed, and one test log from the scheduled execution.
You have successfully scheduled Test Execution for specific tests in your xUnit.net test project using Universal Agent and have it reported the Test Execution to qTest Manager.