Tag Archives: TfsTeamProjectCollectionFactory

Programmatically creating a test case for a work item using the TFS API

Previously I’ve covered how to programmatically create a bug in TFS. In this post, we’ll create a test case to cover it.

Set-up

What we’re going to do here is to create a new test case based on the bug that we created in the linked post, then we’re going to copy key values across, and link the two.

First, we need to do some re-factoring; the section of code the saves the work item can be extracted into something like this:

private static ActionResult CheckValidationResult(WorkItem workItem)
{
    var validationResult = workItem.Validate();
 
    ActionResult result = null;
    if (validationResult.Count == 0)
    {
        // Save the new work item.
        workItem.Save();
 
        result = new ActionResult()
        {
            Success = true,
            Id = workItem.Id
        };
    }
    else
    {
        result = ParseValidation(validationResult);
    }
 
    return result;
}

ActionResult is here for reference:

public class ActionResult
{
    public bool Success { get; set; }
    public List<string> ErrorCodes { get; set; }
    public int Id { get; set; }
}

Let’s re-visit what the code to create the bug looks like now:

public static ActionResult CreateNewBug(string uri, string teamProject, string title, string description,
                string area, string iteration, string assignee, string reproductionSteps)
{
    var project = TFSUtilLibrary.TeamProjectHelper.GetTeamProject(uri, teamProject);
    return CreateNewBug(project, title, description, area, iteration, assignee, reproductionSteps);
}
private static ActionResult CreateNewBug(Project teamProject, string title, string description, 
    string area, string iteration, string assignee, string reproductionSteps)
{
    WorkItemType workItemType = teamProject.WorkItemTypes["Bug"];
 
    // Create the work item. 
    WorkItem newBug = new WorkItem(workItemType);
    newBug.Title = title;
    newBug.Description = description;
    newBug.AreaPath = area;
    newBug.IterationPath = iteration;
    newBug.Fields["Assigned To"].Value = assignee;
 
    newBug.Fields["Repro Steps"].Value = reproductionSteps;
 
    newBug.Fields["Tags"].Value = "Tagtest1, tagtest2, tagtest3";
 
    ActionResult result = CheckValidationResult(newBug);
 
    return result;
}

You’ll notice that I’ve added some tags. Why will become apparent later.

Finding a Work Item

We’re creating a test case for a work item; consequently, we need to be able to retrieve a WorkItem, given an ID:

private static WorkItem GetWorkItem(string uri, int testedWorkItemId)
{
    TfsTeamProjectCollection tfs;
 
    tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(uri)); // https://mytfs.visualstudio.com/DefaultCollection
    tfs.Authenticate();
 
    var workItemStore = new WorkItemStore(tfs);
    WorkItem workItem = workItemStore.GetWorkItem(testedWorkItemId);
 
    return workItem;
 
}

Get a Team Project

The next step is that we’ll need to be able to find a team project (we just will):

public static Project GetTeamProject(string uri, string name)
{
    TfsTeamProjectCollection tfs;
 
    tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(uri)); // https://mytfs.visualstudio.com/DefaultCollection
    tfs.Authenticate();
 
    var workItemStore = new WorkItemStore(tfs);
    
    var project = (from Project pr in workItemStore.Projects
                       where pr.Name == name
                       select pr).FirstOrDefault();
    if (project == null)
        throw new Exception($"Unable to find {name} in {uri}");
 
    return project;
}

Create the Test Case

We now have everything that we need to create the test case. Here’s the parent code that will create the bug:

var result = WorkItemHelper.CreateNewBug(TFSUri, "TFSSandbox",
    "Test new bug", "New bug description", @"TFSSandbox\Team 12", @"TFSSandbox\Iteration 1", "Paul Michaels",
    "Click the screen");
 
if (result.Success)
{
    // Now create the test case
    var resultTestCase = WorkItemHelper.CreateNewTestCase(
        TFSUri, "TFSSandbox", result.Id, "Test case description", "Paul Michaels",
        "reproduction steps here");
}

So, we’re creating a bug and, if it’s successful, we’re creating a test case for it; the CreateNewTestCase code looks like this:

public static ActionResult CreateNewTestCase(string uri, string teamProject, 
        int testedWorkItemId, string description, string assignee, string reproductionSteps)
{
    var project = GetTeamProject(uri, teamProject);
    var workItem = GetWorkItem(uri, testedWorkItemId);
    return CreateNewTestCase(uri, project, workItem, description, assignee, reproductionSteps);
}

private static ActionResult CreateNewTestCase(string uri, Project project, WorkItem testedWorkItem, string description, string assignee, string reproductionSteps)
{
    WorkItemType workItemType = project.WorkItemTypes["Test Case"];
 
    // Create the work item. 
    WorkItem newTestCase = new WorkItem(workItemType);
    newTestCase.Title = $"Test {testedWorkItem.Title}";
    newTestCase.Description = description;
    newTestCase.AreaPath = testedWorkItem.AreaPath;
    newTestCase.IterationPath = testedWorkItem.IterationPath;
    newTestCase.Fields["Assigned To"].Value = assignee;
  
    // Copy tags
    newTestCase.Fields["Tags"].Value = testedWorkItem.Fields["Tags"].Value;
 
    ActionResult result = CheckValidationResult(newTestCase);
    if (result.Success)
    {
        CreateTestedByLink(uri, testedWorkItem, result.Id);
    }
 
    return result;
}

Couple of things to note here; the first is the tags – we’re copying them from the bug (see references). The second is that we are linking the two.

Links

Here’s how I created the link:

private static void CreateTestedByLink(string uri, WorkItem testedWorkItem, int newTestCaseId)
{
    TfsTeamProjectCollection tfs;
 
    tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(uri)); // https://mytfs.visualstudio.com/DefaultCollection
    tfs.Authenticate();
 
    var workItemStore = new WorkItemStore(tfs);
 
    var linkTypes = workItemStore.WorkItemLinkTypes;
 
    WorkItemLinkType testedBy = linkTypes.FirstOrDefault(lt => lt.ForwardEnd.Name == "Tested By");
    WorkItemLinkTypeEnd linkTypeEnd = testedBy.ForwardEnd;
 
    //Add the link as related link.
    testedWorkItem.Links.Add(new RelatedLink(linkTypeEnd, newTestCaseId));
    var result = CheckValidationResult(testedWorkItem);
}

It feels like there might be a slicker way than referencing “Tested By” by name, but this is the only way I could find.

Here’s the created bug with a linked test case:

tfsapi_bug_test

Conclusion

As with the previous post, I’m not trying to re-write TeamCity or anything here; this was just born out of some pain with manually setting these things up.

You’ll also notice that I’ve left the Test Steps; I’ll come back to them shortly (and by ‘shortly’, I mean in a later post).

References

http://blogs.microsoft.co.il/shair/2010/02/27/tfs-api-part-22-create-link-between-work-item-parent-child-etc/

https://social.msdn.microsoft.com/Forums/vstudio/en-US/8d8bfc70-4da7-40ac-ad34-28ab8ef73314/add-tags-programmatically-to-work-items?forum=tfsgeneral

Programmatically create a bug in TFS

If you’re creating a TFS API program from scratch, the first thing that you’ll need is to reference the TFS API libraries. They are in extensions:

tfsbug1

Don’t worry too much about which one’s you’ll need just yet, when you start to write some code, this will be more obvious. The next stage is to create a function that creates your bug; it might look like this:

private static ActionResult CreateNewBug(Project teamProject, string title, string description, 
    string area, string iteration, string assignee, string reproductionSteps)
{
    WorkItemType workItemType = teamProject.WorkItemTypes["Bug"];
 
    // Create the work item. 
    WorkItem newBug = new WorkItem(workItemType);
    newBug.Title = title;
    newBug.Description = description;
    newBug.AreaPath = area;
    newBug.IterationPath = iteration;
    newBug.Fields["Assigned To"].Value = assignee;
 
    newBug.Fields["Repro Steps"].Value = reproductionSteps;
 
    var validationResult = newBug.Validate();
 
    if (validationResult.Count == 0)
    {
        // Save the new work item.
        newBug.Save();
 
        return new ActionResult()
        {
            Success = true
        };
    }
    else
    {
        // Establish why it can't be saved
        var result = new ActionResult()
        {
            Success = false,
            ErrorCodes = new List<string>()                
        };
 
        foreach (var res in validationResult)
        {
            Microsoft.TeamFoundation.WorkItemTracking.Client.Field field = res as Microsoft.TeamFoundation.WorkItemTracking.Client.Field;
            if (field == null)
            {
                result.ErrorCodes.Add(res.ToString());
            }
            else
            {
                result.ErrorCodes.Add($"Error with: {field.Name}");
            }
        }
 
        return result;
    }
}

Obviously, we’re not writing a new front end for TFS here, but the basics are there. The first part of the function gets the relevant fields; once the .Validate() has been called, then we have a look at the result. If there are no errors then just save; otherwise, we try and work out what they were.

In the example above, I’m returning a class of the following type:

public class ActionResult
{
    public bool Success { get; set; }
    public List<string> ErrorCodes { get; set; }
}

But that’s only because this is in its own library. The method above also accepts a Project; assuming that you know what the project is called, you could use something like this to return the correct object:

public static Project GetTeamProject(string uri, string name)
{
    TfsTeamProjectCollection tfs;
 
    tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(uri)); // https://mytfs.visualstudio.com/DefaultCollection
    tfs.Authenticate();
 
    var workItemStore = new WorkItemStore(tfs);
    
    var project = (from Project pr in workItemStore.Projects
                       where pr.Name == name
                       select pr).FirstOrDefault();
    if (project == null)
        throw new Exception($"Unable to find {name} in {uri}");
 
    return project;
}

And that’s it; here’s my calling code:

var result = TFSUtilLibrary.WorkItemHelper.CreateNewBug(TFSUri, "TFSSandbox",
    "Test new bug", "New bug description", @"TFSSandbox\Team 12", @"TFSSandbox\Iteration 1", "Paul Michaels",
    "Click the screen");

Here’s the bug to prove it works:

tfsbug2

tfsbug3

Unable to get IBuildServer – always returning null

I recently had a very strange error while using the TFS API. The error was with the following code:

var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(URI));
var vcs = tfs.GetService<VersionControlServer>();
var buildServer = tfs.GetService<IBuildServer>();                

The problem being that `buildServer` was being set to null. I had the TFS server set-up locally, and there was a controller, an agent and a build configured.

I spent a good while trying to debug, what I thought was, an error with the code.

The Solution

Turns out the problem was with the reference:

TFSBuildRef

The problem was that I was referencing the wrong version (trying to connect to TFS2013 but the build client version was 11.0.0.0). Swapping it to version 12.0.0.0 works fine.

It sounds like a silly mistake, but if you make it, it’s very difficult to spot.