I have an ITestCaseResult
object in hand and I can't figure out how to extract the Test Class information from it. The object contains the test method's name in the TestCaseTitle
property but there are a lot of duplicate titles across our code base and I would like more information.
Assuming I have Foo.Bar
assembly with class Baz
and method ThisIsATestMethod
, I currently only have access to the ThisIsATestMethod
information from the title, but I would like to obtain Foo.Bar.Baz.ThisIsATestMethod
.
How can I do that using the TFS API?
Here's some stripped down code:
var def = buildServer.CreateBuildDetailSpec(teamProject.Name);
def.MaxBuildsPerDefinition = 1;
def.QueryOrder = BuildQueryOrder.FinishTimeDescending;
def.DefinitionSpec.Name = buildDefinition.Name;
def.Status = BuildStatus.Failed | BuildStatus.PartiallySucceeded | BuildStatus.Succeeded;
var build = buildServer.QueryBuilds(def).Builds.SingleOrDefault();
if (build == null)
return;
var testRun = tms.GetTeamProject(teamProject.Name).TestRuns.ByBuild(build.Uri).SingleOrDefault();
if (testRun == null)
return;
foreach (var outcome in new[] { TestOutcome.Error, TestOutcome.Failed, TestOutcome.Inconclusive, TestOutcome.Timeout, TestOutcome.Warning })
ProcessTestResults(bd, testRun, outcome);
...
private void ProcessTestResults(ADBM.BuildDefinition bd, ITestRun testRun, TestOutcome outcome)
{
var results = testRun.QueryResultsByOutcome(outcome);
if (results.Count == 0)
return;
var testResults = from r in results // The "r" in here is an ITestCaseResult. r.GetTestCase() is always null.
select new ADBM.Test() { Title = r.TestCaseTitle, Outcome = outcome.ToString(), ErrorMessage = r.ErrorMessage };
}
You can do this by downloading the TRX file from TFS and parsing it manually. To download the TRX file for a test run, do this:
TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://my-tfs:8080/tfs/DefaultCollection"));
ITestManagementService tms = tpc.GetService<ITestManagementService>();
ITestManagementTeamProject tmtp = tms.GetTeamProject("My Project");
ITestRunHelper testRunHelper = tmtp.TestRuns;
IEnumerable<ITestRun> testRuns = testRunHelper.ByBuild(new Uri("vstfs:///Build/Build/123456"));
var failedRuns = testRuns.Where(run => run.QueryResultsByOutcome(TestOutcome.Failed).Any()).ToList();
failedRuns.First().Attachments[0].DownloadToFile(@"D:\temp\myfile.trx");
Then parse the TRX file (which is XML), looking for the <TestMethod> element, which contains the fully-qualified class name in the "className" attribute:
<TestMethod codeBase="C:/Builds/My.Test.AssemblyName.DLL" adapterTypeName="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestAdapter, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" className="My.Test.ClassName, My.Test.AssemblyName, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" name="Test_Method" />
Since the details of the testcase are stored in the work item you can fetch the data by accessing the work item for the test case
ITestCaseResult result;
var testCase = result.GetTestCase();
testCase.WorkItem["Automated Test Name"]; // fqdn of method
testCase.WorkItem["Automated Test Storage"]; // dll
Here you have a way to get the Assembly name:
foreach (ITestCaseResult testCaseResult in failures)
{
string testName = testCaseResult.TestCaseTitle;
ITmiTestImplementation testImplementation = testCaseResult.Implementation as ITmiTestImplementation;
string assembly = testImplementation.Storage;
}
Unfortunately, ITestCaseResult and ITmiTestImplementation don’t seem to contain the namespace of the test case.
Check the last response in this link, that might help.
Good Luck!
EDIT:
This is based on Charles Crain's answer, but getting the class name without having to download to file:
var className = GetTestClassName(testResult.Attachments);
And the method itself:
private static string GetTestClassName(IAttachmentCollection attachmentCol)
{
if (attachmentCol == null || attachmentCol.Count == 0)
{
return string.Empty;
}
var attachment = attachmentCol.First(att => att.AttachmentType == "TmiTestResultDetail");
var content = new byte[attachment.Length];
attachment.DownloadToArray(content, 0);
var strContent = Encoding.UTF8.GetString(content);
var reader = XmlReader.Create(new StringReader(RemoveTroublesomeCharacters(strContent)));
var root = XElement.Load(reader);
var nameTable = reader.NameTable;
if (nameTable != null)
{
var namespaceManager = new XmlNamespaceManager(nameTable);
namespaceManager.AddNamespace("ns", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010");
var classNameAtt = root.XPathSelectElement("./ns:TestDefinitions/ns:UnitTest[1]/ns:TestMethod[1]", namespaceManager).Attribute("className");
if (classNameAtt != null) return classNameAtt.Value.Split(',')[1].Trim();
}
return string.Empty;
}
internal static string RemoveTroublesomeCharacters(string inString)
{
if (inString == null) return null;
var newString = new StringBuilder();
foreach (var ch in inString)
{
// remove any characters outside the valid UTF-8 range as well as all control characters
// except tabs and new lines
if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r')
{
newString.Append(ch);
}
}
return newString.ToString();
}