In the past, for each class library project we create in Visual Studio, we have maintained separate unit test and integration test projects. This has allowed us to easily configure continuous integration with TFS to run a given project’s unit tests on check-in and run the full suite of unit and integration tests on a nightly build.
With the introduction of Visual Studio 2010, build types now can filter tests based on test categories.
For example, I can categorize a given test as a unit test by doing the following:
[TestClass]
public class Test
{
[TestMethod]
[TestCategory("UnitTest")]
public void IsValid_returns_false_when_quantity_large()
{
}
}
This allows us to consolidate all tests into one project and distinguish by categories. The only issue is that these categories are based on strings that the developer must type, and this can create inconsistencies using magic strings which may not be noticed during the course of development.
In order to get around this, another option is to create your own custom test category attributes that return the proper string. Microsoft has provided a base class, TestCategoryBaseAttribute for this very purpose.
public class UnitTestAttribute : TestCategoryBaseAttribute
{
public UnitTestAttribute()
{}
public override IList<string> TestCategories
{
get { return new List<string> {"UnitTest"}; }
}
}
By overriding the TestCategories property, you can return the appropriate string(s) that represent this classification. In this case, we are returning the same string that the previous test category provided.
In order to use this attribute, you merely need to add it to a given test method like so.
[TestClass]
public class Test
{
[TestMethod]
[UnitTest]
public void IsValid_returns_false_when_quantity_large()
{
}
}
With this kind of flexibility, if you had a limited list of attributes that you wanted to provide to developers, you could also create an enum type and pass that into the constructor of the custom test category attribute.
Just create an enum type.
public enum Category : int
{
UnitTest = 0,
IntegrationTest = 1
}
Then, create the attribute using the enum as a constructor parameter.
public class TestCategoryAttribute : TestCategoryBaseAttribute
{
private TestCategory _category;
public TestCategoryAttribute(TestCategory category)
{
_category = category;
}
public override IList<string> TestCategories
{
get
{
var value = Enum.GetName(typeof (TestCategory), _category);
return new List<string>{ value };
}
}
}
To use the new attribute, you would do the following.
[TestClass]
public class Test
{
[TestMethod]
[TestCategory(Category.UnitTest)]
public void IsValid_returns_false_when_quantity_large()
{
}
}
Hope this helps!
5 comments:
Good stuff, looks strangely familiar!
how to get the string inside [TestCategory(Category.UnitTest)]
I want the string "Category.UnitTest" from the Test category. How will I extract it? Please help . Thank you.
Can you provide more information on why you need the string?
If you create your own TestCategory class, how can you run your test in vstest.console.exe? What TestCaseFilter do you use?
Thanks for your question. When you create your custom category, you have to override the TestCategories property indicating what string or strings you want to return for that category. When you filter, you specify this value in the TestCaseFilter.
If you use a set of enum values as specified in the second example, the test category would return the string value of that enum when the test runner interrogates the TestCategories property. So, if you create an attribute such as [TestCategoryColor(Color.Red)] instead of [TestCategoryColor(Color.Blue)], for example, the TestCaseFilter would be "Red". (This is a ridiculous categorization, but it makes my point.)
I hope that helps. If not, let me know.
Thanks...
Post a Comment