Tuesday, May 7, 2013

Migrate all your project work items to another project : Total TFS Migration Tool

As I mentioned in my first post of  Creating a Test Plans and Cases Migration Tool For TFSWe had to move some team projects from TFS 2010 Server to a newly created TFS 2012 Server. Usually You can restore all your projects by using a tfs backup. But there can be many reasons that will cause you to need a migration tool. In our case our database and backup are both have corrupted. Therefore we were unable to do a restore from backup.

Migration involves moving all source files,work items, attachments, links and team queries. Though moving source files is an easy task, moving work items is not an easy feat. This is due to many reasons. Such as TFS 2010 use the Scrum 1.0 template and TFS 2012  use Scrum 2.0 template(Update 2 use 2.2 template). Migration is done through the APIs of TFS by using external tools, and is a lossy data transfer. There were some migration tools but  it all have certain limitations; such as not supporting test case migration, permission, etc. Therefore we decided to built a custom tool that match our need.

After we finished building a custom tool to match our needs, we thought of developing a generic tool that might help others who have the same need; as there isn't a generic tool to do this. Building a total generic tool for TFS Migration is almost impossible as every company that use TFS as a source control software and as a team management software, have customized it to suit their needs and development methodologies. But we tried to make it as generic as we can. But as the source code is available, anyone can customize this tool to match their requirements. The tool is available at  CodePlex -  Total TFS Migration Tool.



Scope of the tool:
At this point in time, the TFS Migration Tool is scoped to only support the following TFS features:
  • Work Item Migration - This includes the migration of all work items, fields, in-use areas, iterations, links, attachments, shared queries.
  • Test Plan Migration – This includes migration of all test plans and test suits.


Limitations:
  • No Version Control – Project source code will not be transferred.
  • Work item history will not be transferred.

Features:
  • Copy custom fields of relevant work item types in source project to target project. 
  • Copy workflows from source TFS project to target TFS project.
  • Map the mismatched source fields with target fields.
  • Logging mechanism to track entire migration process

How to use the tool is described in full detail in the documentation. Feedback from end users will also be critical to make this more generic, so please help us by sharing your experiences and feedback.

Note:
Tool can be used to copy custom fields and work flows, but if you prefer you can manually copy them using Process Editor and ignore those tabs in the tool as those features are not required by the tool.

Wednesday, May 1, 2013

How To Create a Test Plans and Cases Migration Tool For TFS : Part 4

All my previous posts (part 1, Part 2, and Part 3) have covered the knowledge needed you to build a test plan migration tool for TFS. I put all those together and create a simple WPF project that you can find it here.
Tool has two operational modes.

1) Migration
2) Restore

In Migration mode it will copy test cases from source project to target project as well as test plans and suits.  tool will copy all test plans, suits and test cases from the source project and create Test plans, suits and  cases similar to source project. Not only test cases, it will also copy all test steps into test cases and duplicate shared steps that's linked to test cases. You can choose whether or not to duplicate shared steps. but if you choose not to include  shared test steps, software will not add any shared steps. 

But In Restore mode it will only copy test plans and suits. but it will connect appropriate test cases from querying existing work item collection(Restore mode is when you have all test cases in TFS, but lost test plans and have the option of restore those plans from an older test plan version).  This can be happen as deleting test plans only delete test plans and test suits and test cases will remain as they are work items. Therefore in this mode you won't need restoring test cases, shared steps, attachments.



Migration Mode
Connect to source and target project and select options you need.





























Halfway through the migration



























Migration completed



























check test plan,suit and case hierarchy























Restore Mode




























Limitations

One problem I was unable to solve is including links of Test cases to other work items while copying test cases when operating in Migration mode.
Including attachments part is still in progress. Also I hope to include copying iterations and areas in future. For now all the newly created test cases will be in 'New' status, not in their last recorded states. 

You can find the tool here,
https://tfstestpscexporter.codeplex.com

Wednesday, April 24, 2013

Migrating Sprint Work Items from TFS 2010 sever to TFS 2012 Server

The 'Sprint' work item, used in TFS 2010 with the Scrum 1.0 process template, was removed from the Scrum 2.0 process template of TFS 2012. Sprint work item was used to enter details about sprint dates, goals and retrospective. However in TFS 2012, you can configure the sprint schedule as part of the new Agile Project Management feature, making the Sprint work item redundant in TFS 2012. 


But still you might need the old sprint work item for many reasons. As an example, In my office we had the need to migrate all the work item collections of each team projects from a TFS 2010 server to a TFS 2012 Server. But as Sprint is not a default work item in TFS 2012, we needed to find an alternative ways to achieve this. As far as I figure it out we had four options.


1) Use a Task work item or Product Backlog item adding custom fields to suit to transfer all data of a sprint work item.

2) Use SharePoint integration to store 'Sprint' as a document in TFS 2012 Server.

3) Create a new work item type similar to old Sprint work item type.

4) Import the Sprint work item using Process Editor and export it to the target project in TFS 2012 or via the witadmin-importwitd command.



The first two options are unacceptable. Therefore I am going to concentrate on last two options. 


Create a new work item type

Step 1:
In Visual Studio create a new XML file for your work item type.

Step 2: 
Copy and paste the content of old sprint template XML file in the new XML file and save it.

Step 3:
Move the new XML to the location where  the process template was downloaded and save XML file in the \WorkItem Tracking\TypeDefinitions folder under the name 'Sprint'.


Import the Sprint work item using Process Editor

To work with Process Editor you have to install TFS power tools before hand.

Step 1:
Select TOOLS(Menu) -> Process Editor -> Work Item Types -> Export WIT












Step 2:
Select the TFS server and the project collection.

















Step 3:
Select the project and 'Sprint' work item template.






















You'll be asked to save Sprint process template. Save it, you'll need that to export it into the new TFS 2012 server. 

Step 4:
Import exported 'Sprint' Process template into the target project in TFS 2012 Server.





















Note: If you have any problem in importing first remove required fields in the template, and try importing again.

The other way to do this is using witadmin commands. If you want to try witadmin commands, here try it.

Saturday, April 20, 2013

How To Create a Test Plans and Cases Migration Tool For TFS : Part 3 - Duplicating Shared steps and action steps.

In my last post - How To Create a Test Plans and Cases Migration Tool For TFS : Part 2,  I showed how to migrate Test plans, suits, cases from one project to another, programmatically using Test Management API. In this post I'll show how to copy test steps of each test cases including shared steps. 

First of all we iterate all test steps of a test case. Then we try to see whether there is a shared reference for that test step. if it has an reference then it is a shared step, otherwise it's just a normal test step.

Code for duplication of test steps:
                   
                foreach (ITestAction action in testcase.TestCase.Actions)
                {

                    ISharedStep shared_step = null;
                    ISharedStepReference shared_ref = action as ISharedStepReference;
                    if (shared_ref != null)
                    {
                        shared_step = shared_ref.FindSharedStep();
                        foreach (ITestAction shr_action in shared_step.Actions)
                        {
                            ITestStep test_step = shr_action as ITestStep; ;
                            ITestStep destinationStep = tc.CreateTestStep();
                            destinationStep.Title = test_step.Title;
                            destinationStep.ExpectedResult = test_step.ExpectedResult;
                            tc.Actions.Add(destinationStep);
                        }

                    }
                    else
                    {
                        var test_step = action as ITestStep;
                        ITestStep destinationStep = tc.CreateTestStep();
                        destinationStep.Title = test_step.Title;
                        tc.Actions.Add(destinationStep);
                    }
                }



Full Code:

 public partial class MainWindow : Window
    {
        string sourceserverurl = "http://gandalf:8080/tfs/defaultcollection";
        string sourceproject = "Sample Project";
        string destinationtfs = "http://aragon:8080/tfs/defaultcollection";
        string destinationproject = "PMTest";
        ITestManagementTeamProject sourceproj;
        ITestManagementTeamProject destinationproj;    

        public MainWindow()
        {
            InitializeComponent();
            sourceproj = GetProject(sourceserverurl, sourceproject);
            destinationproj = GetProject(destinationtfs, destinationproject);
        }

        private ITestManagementTeamProject GetProject(string serverUrl, string project)
        {
            TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(serverUrl));
            ITestManagementService tms = tfs.GetService<ITestManagementService>();

            return tms.GetTeamProject(project);
        }

        void CopyTestPlans()
        {

            foreach (ITestPlan destinationplan in destinationproj.TestPlans.Query("Select * From TestPlan"))
            {

                System.Diagnostics.Debug.WriteLine("Deleting Plan - {0} : {1}", destinationplan.Id, destinationplan.Name);

                destinationplan.Delete(DeleteAction.ForceDeletion); ;

            }

            foreach (ITestPlan sourceplan in sourceproj.TestPlans.Query("Select * From TestPlan"))
            {
                System.Diagnostics.Debug.WriteLine("Plan - {0} : {1}", sourceplan.Id, sourceplan.Name);

                ITestPlan destinationplan = destinationproj.TestPlans.Create();
                destinationplan.Name = sourceplan.Name;
                destinationplan.Description = sourceplan.Description;
                destinationplan.StartDate = sourceplan.StartDate;
                destinationplan.EndDate = sourceplan.EndDate;
                destinationplan.State = sourceplan.State;

                if (sourceplan.RootSuite != null && sourceplan.RootSuite.Entries.Count > 0)
                {
                    CopyTestSuites(sourceplan, destinationplan);
                }

                destinationplan.Save();
            }
        }

        void CopyTestSuites(ITestPlan sourceplan, ITestPlan destinationplan)
        {
            ITestSuiteEntryCollection suites = sourceplan.RootSuite.Entries;

            foreach (ITestSuiteEntry suite_entry in suites)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite newSuite = destinationproj.TestSuites.CreateStatic();
                    newSuite.Title = suite.Title;
                    destinationplan.RootSuite.Entries.Add(newSuite); 
                    destinationplan.Save();

                    CopyTestCases(suite, newSuite);
                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, newSuite);
                }
            }
        }

        void CopySubTestSuites(IStaticTestSuite parentsourceSuite, IStaticTestSuite parentdestinationSuite)
        {
            ITestSuiteEntryCollection suitcollection = parentsourceSuite.Entries;
            foreach (ITestSuiteEntry suite_entry in suitcollection)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite subSuite = destinationproj.TestSuites.CreateStatic();
                    subSuite.Title = suite.Title;
                    parentdestinationSuite.Entries.Add(subSuite);

                    CopyTestCases(suite, subSuite);

                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, subSuite);

                }     
            }          
        }

        void CopyTestCases(IStaticTestSuite sourcesuite, IStaticTestSuite destinationsuite)
        {
            ITestCaseCollection dd=sourcesuite.AllTestCases;
            ITestSuiteEntryCollection suiteentrys = sourcesuite.TestCases;

            foreach (ITestSuiteEntry testcase in suiteentrys)
            {
                ITestCase tc = destinationproj.TestCases.Create();
                tc.Title = testcase.Title;
                tc.Priority = testcase.TestCase.Priority;
               
                 foreach (ITestAction action in testcase.TestCase.Actions)
                {
                    ISharedStep shared_step = null;
                    ISharedStepReference shared_ref = action as ISharedStepReference;
                    if (shared_ref != null)
                    {
                        shared_step = shared_ref.FindSharedStep();
                        foreach (ITestAction shr_action in shared_step.Actions)
                        {
                            ITestStep test_step = shr_action as ITestStep; ;
                            ITestStep destinationStep = tc.CreateTestStep();
                            destinationStep.Title = test_step.Title;
                            destinationStep.ExpectedResult = test_step.ExpectedResult;
                            tc.Actions.Add(destinationStep);
                        }

                    }
                    else
                    {
                        var test_step = action as ITestStep;
                        ITestStep destinationStep = tc.CreateTestStep();
                        destinationStep.Title = test_step.Title;
                        tc.Actions.Add(destinationStep);
                    }
                }
                tc.Save();
                destinationsuite.Entries.Add(tc);
            }
        }

        private void btn_connect_Click(object sender, RoutedEventArgs e)
        {
            CopyTestPlans();
        }
        
    }


Thursday, April 18, 2013

How To Create a Test Plans and Cases Migration Tool For TFS : Part 2 - Copy Test Plans, Suits and Test cases.

In my last post -How To Create a Test Plans and Cases Migration Tool For TFS : Part 1,  I showed how to create and manage Test plans, suits, cases programmatically using Test Management API. The knowledge of basics is necessary in order to understand the functionality of the Test Plans and Cases migration tool we intended to build. In this post let's focus on about how to get all the test suits and test cases in a test plan. This is bit tricky as test suits can be nested within test suits. 
Once again you’ll need to add references to the following assemblies to your project.
  • Microsoft.TeamFoundation.Client.dll 
  • Microsoft.TeamFoundation.TestManagement.Client.dll 
  • Microsoft.TeamFoundation.TestManagement.Common.dll 
  • Microsoft.TeamFoundation.WorkItemTracking.Client.dll 
These assemblies are located at:C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ReferenceAssemblies\v2.0


Step 1: Connect to source and target projects 

        string sourceserverurl = "http://gandalf:8080/tfs/defaultcollection";
        string sourceproject = "Sample Project";
        string destinationtfs = "http://aragon:8080/tfs/defaultcollection";
        string destinationproject = "PMTest";

        ITestManagementTeamProject sourceproj;
        ITestManagementTeamProject destinationproj;
        sourceproj = GetProject(sourceserverurl, sourceproject);
        destinationproj = GetProject(destinationtfs, destinationproject);


ITestManagementTeamProject GetProject(string serverUrl, string project)
{
  TfsTeamProjectCollection tfs = new  TfsTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(serverUrl));
  ITestManagementService tms = tfs.GetService<ITestManagementService>();

   return tms.GetTeamProject(project);
}


Step 2: Copy Test Plans from source to target project

foreach (ITestPlan sourceplan in sourceproj.TestPlans.Query("Select * From TestPlan"))
            {
                ITestPlan destinationplan = destinationproj.TestPlans.Create();
                destinationplan.Name = sourceplan.Name;
                destinationplan.Description = sourceplan.Description;
                destinationplan.StartDate = sourceplan.StartDate;
                destinationplan.EndDate = sourceplan.EndDate;
                destinationplan.State = sourceplan.State;
                destinationplan.Save();
            }

Step 3: Check whether any root test suits exist for a test plan

 if (sourceplan.RootSuite != null && sourceplan.RootSuite.Entries.Count > 0)
{
                    CopyTestSuites(sourceplan, destinationplan);
}

Step 4: Copy Root Test  suits from source  to target plan

void CopyTestSuites(ITestPlan sourceplan, ITestPlan destinationplan)
        {
            ITestSuiteEntryCollection suites = sourceplan.RootSuite.Entries;

            foreach (ITestSuiteEntry suite_entry in suites)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite newSuite = destinationproj.TestSuites.CreateStatic();
                    newSuite.Title = suite.Title;
                    destinationplan.RootSuite.Entries.Add(newSuite); 
                    destinationplan.Save();

                   //Check whether root suite contains nested suits
                    CopyTestCases(suite, newSuite);
                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, newSuite);
                }
            }

        }

Step 5: Recursively copy nested Test suits 

 void CopySubTestSuites(IStaticTestSuite parentsourceSuite, IStaticTestSuite parentdestinationSuite)
        {
            ITestSuiteEntryCollection suitcollection = parentsourceSuite.Entries;
            foreach (ITestSuiteEntry suite_entry in suitcollection)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite subSuite = destinationproj.TestSuites.CreateStatic();
                    subSuite.Title = suite.Title;
                    parentdestinationSuite.Entries.Add(subSuite);
                    // call to copy test cases that contains in the leaf test suite.
                    CopyTestCases(suite, subSuite);

                    // check whether if test suit contains nested test suits
                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, subSuite);
                }     
            }        
        }

Step 6: Copy Test cases from source to target plan

 void CopyTestCases(IStaticTestSuite sourcesuite, IStaticTestSuite destinationsuite)
        {
            ITestCaseCollection dd=sourcesuite.AllTestCases;
            ITestSuiteEntryCollection suiteentrys = sourcesuite.TestCases;

            foreach (ITestSuiteEntry testcase in suiteentrys)
            {
                ITestCase tc = destinationproj.TestCases.Create();
                tc.Title = testcase.Title;
                tc.Priority = testcase.TestCase.Priority;
                tc.Save();
                destinationsuite.Entries.Add(tc);
            }
        }

Full Code:

 public partial class MainWindow : Window
    {
        string sourceserverurl = "http://gandalf:8080/tfs/defaultcollection";
        string sourceproject = "Sample Project";
        string destinationtfs = "http://zeus:8080/tfs/defaultcollection";
        string destinationproject = "PMTest";
        ITestManagementTeamProject sourceproj;
        ITestManagementTeamProject destinationproj;    

        public MainWindow()
        {
            InitializeComponent();
            sourceproj = GetProject(sourceserverurl, sourceproject);
            destinationproj = GetProject(destinationtfs, destinationproject);
        }

        private ITestManagementTeamProject GetProject(string serverUrl, string project)
        {
            TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(TfsTeamProjectCollection.GetFullyQualifiedUriForName(serverUrl));
            ITestManagementService tms = tfs.GetService<ITestManagementService>();

            return tms.GetTeamProject(project);
        }

        void CopyTestPlans()
        {

            foreach (ITestPlan destinationplan in destinationproj.TestPlans.Query("Select * From TestPlan"))
            {

                System.Diagnostics.Debug.WriteLine("Deleting Plan - {0} : {1}", destinationplan.Id, destinationplan.Name);

                destinationplan.Delete(DeleteAction.ForceDeletion); ;

            }

            foreach (ITestPlan sourceplan in sourceproj.TestPlans.Query("Select * From TestPlan"))
            {
                System.Diagnostics.Debug.WriteLine("Plan - {0} : {1}", sourceplan.Id, sourceplan.Name);

                ITestPlan destinationplan = destinationproj.TestPlans.Create();
                destinationplan.Name = sourceplan.Name;
                destinationplan.Description = sourceplan.Description;
                destinationplan.StartDate = sourceplan.StartDate;
                destinationplan.EndDate = sourceplan.EndDate;
                destinationplan.State = sourceplan.State;

                if (sourceplan.RootSuite != null && sourceplan.RootSuite.Entries.Count > 0)
                {
                    CopyTestSuites(sourceplan, destinationplan);
                }

                destinationplan.Save();
            }
        }

        void CopyTestSuites(ITestPlan sourceplan, ITestPlan destinationplan)
        {
            ITestSuiteEntryCollection suites = sourceplan.RootSuite.Entries;

            foreach (ITestSuiteEntry suite_entry in suites)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite newSuite = destinationproj.TestSuites.CreateStatic();
                    newSuite.Title = suite.Title;
                    destinationplan.RootSuite.Entries.Add(newSuite); 
                    destinationplan.Save();

                    CopyTestCases(suite, newSuite);
                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, newSuite);
                }
            }
        }

        void CopySubTestSuites(IStaticTestSuite parentsourceSuite, IStaticTestSuite parentdestinationSuite)
        {
            ITestSuiteEntryCollection suitcollection = parentsourceSuite.Entries;
            foreach (ITestSuiteEntry suite_entry in suitcollection)
            {
                IStaticTestSuite suite = suite_entry.TestSuite as IStaticTestSuite;
                if (suite != null)
                {
                    IStaticTestSuite subSuite = destinationproj.TestSuites.CreateStatic();
                    subSuite.Title = suite.Title;
                    parentdestinationSuite.Entries.Add(subSuite);

                    CopyTestCases(suite, subSuite);

                    if (suite.Entries.Count > 0)
                        CopySubTestSuites(suite, subSuite);

                }     
            }          
        }

        void CopyTestCases(IStaticTestSuite sourcesuite, IStaticTestSuite destinationsuite)
        {
            ITestCaseCollection dd=sourcesuite.AllTestCases;
            ITestSuiteEntryCollection suiteentrys = sourcesuite.TestCases;

            foreach (ITestSuiteEntry testcase in suiteentrys)
            {
                ITestCase tc = destinationproj.TestCases.Create();
                tc.Title = testcase.Title;
                tc.Priority = testcase.TestCase.Priority;
                tc.Save();
                destinationsuite.Entries.Add(tc);
            }
        }

        private void btn_connect_Click(object sender, RoutedEventArgs e)
        {
            CopyTestPlans();
        }
        
    }


Next time let's discuss how to add action steps and  duplicate shared steps of test cases in target server.