Extending the Visual Studio Unit Test Type, part 2
In part 1, we introduced Unit Test Type Extensibility, highlighting the main parts of the interface. In this round, we will discuss three additional pieces:
- Adding a user interface
- Parameterizing the test method
- Aggregating test results
- UI extension
Due to the amount of code in this sample, I will paste some of it at the end of the blog. The rest I will try to upload as a file to the blog.
The Finished Product
Lets begin again at the end. “What do these three additional pieces give me?”
A snapshot seems appropriate here
Yes, I know, unit test should already have this built into the standard implementation, but since we have not done this, one is able to build a very quick implementation of it for your own testing.
Lets go through this code.
- The RowTestClass attribute tells the framework to load my test type extension to run these tests
- We have a new custom attribute, called Row, that will allow me to pass in the data that I want to send to my parameterized test method
- Now, with our test method being parameterized, we can use those variables directly in our test method instead of having to pull them off the DataRow in the TestContext
Lets blaze on ahead with the different files. I will quickly show the References, TestClassAttribute, TestExtensionExecution, and parts of the Invoker. We will dive deeper into the invoker, aggregation and UI code.
Row Test Test Type Extension : References
First we need the references. The Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly can be found in the Global Assembly Cache, and the other three Microsoft assemblies can be found in PrivateAssemblies. For more information, see Extending the Visual Studio Unit Test Type, part 1: Run As Extension : References section
Row Test Test Type Extension : RowTestClassAttribute.cs file (quick view)
File number 1, RowTestClassAttribute.cs, is similar to the RunAsTestClassAttribute with the following differences:
- The Uri is unique to this attribute.
- In our GetClientSide implementation, we return a RowTestExtensionClientSide object. (see Row Test Test Type Extension : RowTestExtensionClientSide.cs file)
For more information, see Extending the Visual Studio Unit Test Type, part 1 for more information regarding the TestClassExtensionAttribute.
Row Test Test Type Extension : RowTestExtension.cs file (quick view)
File number 2, RowTestExtension.cs, is similar to the RunAsTestExtensionExecution.cs file and there only difference is the object type returned, which is a RowTestInvoker class (see Row Test Test Type Extension : RowTestInvoker.cs file (quick view) )
Row Test Test Type Extension : RowTestInvoker.cs file (quick view)
File number 3, RowTestInvoker.cs, is similar to the RunAsTestMethodInvoker.cs, with the difference being in the Invoke method.
The Invoke method is where it gets interesting.
- First we initialize our RowTestResults class. This class is used to aggregate our results when we execute them.
- We then gather all the RowAttributes off the test method using reflection.
- We then check the count of the RowAttributes we have. If we don’t have any, we will invoke the base method passing a null as a parameter.
- If we do find a list of RowAttributes, we will iterate through them and call the Invoke method, passing the row information. We also aggregate them into our the RowTestResults instance that we created in step #1.
- Finally we return the single aggregated results
Row Test Test Type Extension : RowTestExtensionClientSide.cs file (quick view)
The Icon property returns an embedded Icon that I placed in the Resources for the project.
The ResultExtensionViewer returns the user control that we have created to display our results.
Row Test Test Type Extension : RowTestExtensionViewer.cs file (quick view)
This is a user control, so it also has a .Designer.cs and a .resx file with it. However, the most important is the implementation of the User control. It must implement ITestTypeExtensionResultViewer
Of note, the Initialize function is what makes this work.
- The initialize takes a TestResult argument. This is will contain quite a bit of information regarding outcome, error, stack trace and other items you may need for that result. But keep in mind, this is the overall result. You can call it the result container.
- For our purpose we need to get a little piece of information off it regarding the ITestResultExtension. So we cast it, check that it isn’t null and dump the results to it (since we returned a string earlier (see Row Test Test Type Extension : RowTestInvoker.cs file – Step #5).
This line also assumes that we have placed a label on our UserControl.
And that is it. A little more work but now we are ready to deploy
Row Test Test Type Extension : Registering your test type extension
To make it all work you now need to place your test type extension assembly in the correct location and register your test type in the registry.
Location: The location of your test type, and your PDB if you want, needs to be in the PrivateAssemblies location; the same one that contains the assemblies that we referenced in the beginning
Registry: The test type needs to be in the following key:
The registry entry is a string called “AttributeProvider” and will contain your attribute and the name of your assembly.
There is a second location that is usually populated when you start Visual Studio. It is located under the following key: (NOTE the bolded text for the differences in the two keys)
I place the second key in manually because sometimes it doesn’t do it for me straight away and I get impatient and cannot wait for the key to be updated. The full registry file can be found at the end of the blog as RowTestExtension.reg
FINAL REGISTRY NOTE: If you are on an x64 machine, the registry key will be under the Wow6432Node as in HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft…
FINAL LOCATION NOTE: If you are on an x64 machine, the location key will be under the Program Files(x86) folder.
WORKING WITH VS NOTE: Because the test type is loaded within Visual Studio, developing the test type in addition to testing it poses problems when you need to update the test type extension assembly. To make this work so you can develop in one instance of Visual Studio and test in a separate instance of Visual Studio, you can use the Visual Studio Development Experimental Model found here
Good Luck! Let us know if there are any questions or thoughts around this. Hopefully extending the unit test type will be far easier than doing it from scratch like in Visual Studio 2008. Doing a test type from scratch is still supported and sometimes needed, but if you can get along by using the test type extension, things will be much easier.
Visual Studio Team Test
We now have our SDK available and code can be found here when it goes live.
The download is found on code gallery here.