Accessing test case parameters in an associated automation

Recently I ran into the following situation at a customer that is implementing Team Foundation Server as the ALM tool of choice.
A BI team had developed a so called “screening framework” which they use to automatically test the SSIS packages they build. The screening framework they build allowed them to specify, store and run tests using a web interface. However the team also started using Team Foundation Server (TFS) and Microsoft Test Manager (MTM) and they really wanted to integrate their custom testing framework with TFS.
As the testers were also using MTM to run manual tests it would be great if the testers were able to run the automated tests from within MTM also by simply clicking the “Run” button. TFS allows you to link a piece of code to your test case, also known as an associated automation, so achieving the above should be quite straight forward. This would also allow them to use their custom in a “Lab Build” in the future, something like this should work:
[TestMethod] public void RunScreeningWithId1() { ScreeningTest test = new ScreeningTest(1); bool result = test.Run(); Assert.IsTrue(result); }
However this would mean that for every test you want to run a new test method must be created. TFS allows you to enter parameters in a test case, wouldn’t it be great if we could pass these parameter to our test method? We could then create a single test method to run a test and use the test parameter to pass the id of the actual test.
Since Visual Studio 2012 Update 1 the Test Controller and Test Agent post a wealth of TFS related information in the TestContext class, as my colleague Marcel de Vries describes in this post. So lets take my previous example to the next level and retrieve the test case associated with our test method from TFS.
When a test method is executed TFS using a test agent the TestContext object’s property dictionary is filled with TFS related information, Marcel posted an elaborate table but we are interested in the following 3 properties for this scenario:
Property | Value |
__Tfs_TestCaseId__ | 1 |
__Tfs_TeamProject__ | TestProject |
__Tfs_TfsServerCollectionUrl | http://localhost:8080/tfs/DefaultCollection |
We can access these properties, retrieve the test case from TFS and print the values for a parameter using the following piece of code:
[Test Method] public void TestMethod() { //just to be safe if(TestContext.Properties["__Tfs_TestCaseId__"] == null) throw new ArgumentNullException("__Tfs_TestCaseId__"); if(TestContext.Properties["__Tfs_TeamProject__"] == null) throw new ArgumentNullException("__Tfs_TeamProject__"); if(TestContext.Properties["__Tfs_TfsServerCollectionUrl__"] == null) throw new ArgumentNullException("__Tfs_TfsServerCollectionUrl__"); int testCaseId = Convert.ToInt32(TestContext.Properties["__Tfs_TestCaseId__"]); string teamProject = TestContext.Properties["__Tfs_TeamProject__"]; string collectionUrl = TestContext.Properties["__Tfs_TfsServerCollectionUrl__"]; //We'll get to this method in a bit ITestManagementTeamProject testManagementTeamProject = GetTeamProjectTestManagementTeamProject(collectionUrl, teamProject) ITestCase testCase = testManagementTeamProject.TestCases.Find(testCaseId); //We'll get to this method in a bit PrintParameterValues(testCase, "parameter1"); }
In the example above I used this helper method to connect to TFS and retrieve an instance of “ITestManagementTeamProject”:
private static ITestManagementTeamProject GetTeamProjectTestManagementTeamProject(string tpcUri, string teamProjectName) { TfsTeamProjectCollection collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tpcUri)); collection.Connect(Microsoft.TeamFoundation.Framework.Common.ConnectOptions.IncludeServices); ITestManagementService service= collection.GetService<ITestManagementService>(); ITestManagementProject project = service.GetTeamProject(teamProjectName); return project; }
And this helper method to print the parameter values:
private void PrintParameterValues(ITestCase testCase, string parameterName) { foreach(DataRow row in testCase.DefaultTableReadOnly.Rows) { string value = row[parameterName]; Console.WriteLine(parameterName + " value: " + value; } }
The code above is quite straight forward, using the Client Object Model of TFS we are able to easily retrieve the required information from TFS. One thing to keep in mind when using this code, the test agent executing this code must run under an account that has at least read permission on the team project you are trying to read test cases from.
To wrap up this post, for the BI team I made a wrapper class wrapping ITestCase and exposing a method to retrieve the parameter values for a specific parameter. Also I created an extension method for the TestContext object so they could do something like the following:
TestCaseWrapper wrapper = TestContext.GetTfsTestCase() wrapper.GetParameterValues("parametername")
Update:
There seem to be some issues with how TFS publishes information into the TestContext, this leads to issues when you run multiple tests at once as indicated by Ish (thanks for the heads-up!). Basically you will only ever get one TestCaseId back in the TestContext, even if you run multiple tests as part of a test run. A colleague of mine ran into the same issue a few weeks aggo and already found a workaround for it. Check out his post: