Tag Archives: Bug

Change a TFS Work Item Definition

Let’s assume that the built-in TFS standard templates are not sufficient for you. You’re in luck: TFS allows you to create your own custom work item. Let’s imagine that, for some reason, you want a work item type called “Defect”, rather than “Bug”. Here’s the process, based on the “Bug” work item type.

First thing is to open the command prompt in administrator mode, and navigate to a work directory; for example, the “Documents” folder
.
Then export the template work item type, like so (you can export the entire definition of all work items, but it becomes unmanageable):

witadmin exportwitd /collection:http://tlaptop:8080/tfs/DefaultCollection /p:"TFSSandbox" /n:Bug /f:Defect.xml

customwi1

Check your working directory:

customwi2

Now, open the XML file. I used Notepad++, but you can use Notepad or vi, or whatever. To change the work item, change the name attribute; once you’ve done this, you have a new work item type:

customwi3

Now, you can add the fields that you want. I’m adding a field called “pcm.CustomField”:

<?xml version="1.0" encoding="utf-8"?>
<witd:WITD application="Work item type editor" version="1.0" xmlns:witd="http://schemas.microsoft.com/VisualStudio/2008/workitemtracking/typedef">
  <WORKITEMTYPE name="Defect">
    <DESCRIPTION>Customised defect</DESCRIPTION>
    <FIELDS>
      <FIELD name="Iteration Path" refname="System.IterationPath" type="TreePath" reportable="dimension">
        <HELPTEXT>The iteration within which this bug will be fixed</HELPTEXT>
      </FIELD>
	  <FIELD name="New Field" refname="pcm.CustomField" type="Integer" />
      <FIELD name="Iteration ID" refname="System.IterationId" type="Integer" />

The field name attribute (“New Field” in this case) tells TFS how to refer to this field to the user. This is important, because if you forget that you’ve called it “New Field”, you might assume this hasn’t worked and start googling to find out why.

Now you’ve told TFS that you have a field to store; the next step is to add that field to the layout:

    <FORM>
      <Layout HideReadOnlyEmptyFields="true" HideControlBorders="true">
        <Group Margin="(4,0,0,0)">
          <Column PercentWidth="90">
            <Control FieldName="System.Title" Type="FieldControl" ControlFontSize="large" EmptyText="&lt;Enter title here&gt;" />
          </Column>
          <Column PercentWidth="10">
            <Control FieldName="System.ID" Type="FieldControl" ControlFontSize="large" />
          </Column>
        </Group>
     <Group Margin="(10,0,0,0)">
          <Column PercentWidth="60">
            <Control FieldName="pcm.CustomField" Type="FieldControl" Label="Custom Field Here" ControlFontSize="large" EmptyText="&lt;Custom field here&gt;" />
          </Column>		
    </Group>
        <Group Margin="(10,0,0,0)">

As you can see, we can move this about and pretty much show anything we want here. Next, re-import:

witadmin importwitd /collection:http://tlaptop:8080/tfs/DefaultCollection /p:TFSSandbox /f:Defect.xml

customwi5

Using the new Work Item Type

And now we have a new work item type:

customwi6

And the new field is available:

customwi7

Using The Field in a Query

customwi8

References

Tutorial on modifying Work Item States

Tutorial on customising TFS 2013

Detailed definition of the FIELD tag

Turotial on modifying the FIELD tag

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