Tutorial: Test a .NET class library using Visual Studio Code
This tutorial shows how to automate unit testing by adding a test project to a solution.
Prerequisites
- This tutorial works with the solution that you create in Create a .NET class library using Visual Studio Code.
Create a unit test project
Unit tests provide automated software testing during your development and publishing. The testing framework that you use in this tutorial is MSTest. MSTest is one of three test frameworks you can choose from. The others are xUnit and nUnit.
Start Visual Studio Code.
Open the
ClassLibraryProjects
solution you created in Create a .NET class library using Visual Studio Code.Create a unit test project named "StringLibraryTest".
dotnet new mstest -o StringLibraryTest
The project template creates a UnitTest1.cs file with the following code:
namespace StringLibraryTest; [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { } }
The source code created by the unit test template does the following:
- It applies the TestClassAttribute attribute to the
UnitTest1
class. - It applies the TestMethodAttribute attribute to define
TestMethod1
. - It imports the Microsoft.VisualStudio.TestTools.UnitTesting namespace, which contains the types used for unit testing. The namespace is imported via a
global using
directive in GlobalUsings.cs.
Each method tagged with [TestMethod] in a test class tagged with [TestClass] is run automatically when the unit test is invoked.
- It applies the TestClassAttribute attribute to the
Add the test project to the solution.
dotnet sln add StringLibraryTest/StringLibraryTest.csproj
Add a project reference
For the test project to work with the StringLibrary
class, add a reference in the StringLibraryTest
project to the StringLibrary
project.
Run the following command:
dotnet add StringLibraryTest/StringLibraryTest.csproj reference StringLibrary/StringLibrary.csproj
Add and run unit test methods
When Visual Studio invokes a unit test, it runs each method that is marked with the TestMethodAttribute attribute in a class that is marked with the TestClassAttribute attribute. A test method ends when the first failure is found or when all tests contained in the method have succeeded.
The most common tests call members of the Assert class. Many assert methods include at least two parameters, one of which is the expected test result and the other of which is the actual test result. Some of the Assert
class's most frequently called methods are shown in the following table:
Assert methods | Function |
---|---|
Assert.AreEqual |
Verifies that two values or objects are equal. The assert fails if the values or objects aren't equal. |
Assert.AreSame |
Verifies that two object variables refer to the same object. The assert fails if the variables refer to different objects. |
Assert.IsFalse |
Verifies that a condition is false . The assert fails if the condition is true . |
Assert.IsNotNull |
Verifies that an object isn't null . The assert fails if the object is null . |
You can also use the Assert.ThrowsException method in a test method to indicate the type of exception it's expected to throw. The test fails if the specified exception isn't thrown.
In testing the StringLibrary.StartsWithUpper
method, you want to provide a number of strings that begin with an uppercase character. You expect the method to return true
in these cases, so you can call the Assert.IsTrue method. Similarly, you want to provide a number of strings that begin with something other than an uppercase character. You expect the method to return false
in these cases, so you can call the Assert.IsFalse method.
Since your library method handles strings, you also want to make sure that it successfully handles an empty string (String.Empty
) and a null
string. An empty string is one that has no characters and whose Length is 0. A null
string is one that hasn't been initialized. You can call StartsWithUpper
directly as a static method and pass a single String argument. Or you can call StartsWithUpper
as an extension method on a string
variable assigned to null
.
You'll define three methods, each of which calls an Assert method for each element in a string array. You'll call a method overload that lets you specify an error message to be displayed in case of test failure. The message identifies the string that caused the failure.
To create the test methods:
Open StringLibraryTest/UnitTest1.cs and replace all of the code with the following code.
using Microsoft.VisualStudio.TestTools.UnitTesting; using UtilityLibraries; namespace StringLibraryTest; [TestClass] public class UnitTest1 { [TestMethod] public void TestStartsWithUpper() { // Tests that we expect to return true. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsTrue(result, string.Format("Expected for '{0}': true; Actual: {1}", word, result)); } } [TestMethod] public void TestDoesNotStartWithUpper() { // Tests that we expect to return false. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word, result)); } } [TestMethod] public void DirectCallWithNullOrEmpty() { // Tests that we expect to return false. string?[] words = { string.Empty, null }; foreach (var word in words) { bool result = StringLibrary.StartsWithUpper(word); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word == null ? "<null>" : word, result)); } } }
The test of uppercase characters in the
TestStartsWithUpper
method includes the Greek capital letter alpha (U+0391) and the Cyrillic capital letter EM (U+041C). The test of lowercase characters in theTestDoesNotStartWithUpper
method includes the Greek small letter alpha (U+03B1) and the Cyrillic small letter Ghe (U+0433).Save your changes.
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that all tests passed.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 3 ms - StringLibraryTest.dll (net8.0)
Handle test failures
If you're doing test-driven development (TDD), you write tests first and they fail the first time you run them. Then you add code to the app that makes the test succeed. For this tutorial, you created the test after writing the app code that it validates, so you haven't seen the test fail. To validate that a test fails when you expect it to fail, add an invalid value to the test input.
Modify the
words
array in theTestDoesNotStartWithUpper
method to include the string "Error".string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " };
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that one test fails, and it provides an error message for the failed test: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Because of the failure, no strings in the array after "Error" were tested.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Failed TestDoesNotStartWithUpper [28 ms] Error Message: Assert.IsFalse failed. Expected for 'Error': false; Actual: True Stack Trace: at StringLibraryTest.UnitTest1.TestDoesNotStartWithUpper() in C:\ClassLibraryProjects\StringLibraryTest\UnitTest1.cs:line 33 Failed! - Failed: 1, Passed: 2, Skipped: 0, Total: 3, Duration: 31 ms - StringLibraryTest.dll (net5.0)
Remove the string "Error" that you added in step 1. Rerun the test and the tests pass.
Test the Release version of the library
Now that the tests have all passed when running the Debug build of the library, run the tests an additional time against the Release build of the library. A number of factors, including compiler optimizations, can sometimes produce different behavior between Debug and Release builds.
Run the tests with the Release build configuration:
dotnet test StringLibraryTest/StringLibraryTest.csproj --configuration Release
The tests pass.
Debug tests
If you're using Visual Studio Code as your IDE, you can use the same process shown in Debug a .NET console application using Visual Studio Code to debug code using your unit test project. Instead of starting the ShowCase app project, open StringLibraryTest/UnitTest1.cs, and select Debug All Tests between lines 7 and 8. If you're unable to find it, press Ctrl+Shift+P to open the command palette and enter Reload Window.
Visual Studio Code starts the test project with the debugger attached. Execution will stop at any breakpoint you've added to the test project or the underlying library code.
Additional resources
Next steps
In this tutorial, you unit tested a class library. You can make the library available to others by publishing it to NuGet as a package. To learn how, follow a NuGet tutorial:
If you publish a library as a NuGet package, others can install and use it. To learn how, follow a NuGet tutorial:
A library doesn't have to be distributed as a package. It can be bundled with a console app that uses it. To learn how to publish a console app, see the earlier tutorial in this series:
The Visual Studio Code extension C# Dev Kit provides more tools for developing C# apps and libraries:
This tutorial shows how to automate unit testing by adding a test project to a solution.
Prerequisites
- This tutorial works with the solution that you create in Create a .NET class library using Visual Studio Code.
Create a unit test project
Unit tests provide automated software testing during your development and publishing. The testing framework that you use in this tutorial is MSTest. MSTest is one of three test frameworks you can choose from. The others are xUnit and nUnit.
Start Visual Studio Code.
Open the
ClassLibraryProjects
solution you created in Create a .NET class library using Visual Studio Code.Create a unit test project named "StringLibraryTest".
dotnet new mstest -o StringLibraryTest
The project template creates a UnitTest1.cs file with the following code:
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace StringLibraryTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { } } }
The source code created by the unit test template does the following:
- It imports the Microsoft.VisualStudio.TestTools.UnitTesting namespace, which contains the types used for unit testing.
- It applies the TestClassAttribute attribute to the
UnitTest1
class. - It applies the TestMethodAttribute attribute to define
TestMethod1
.
Each method tagged with [TestMethod] in a test class tagged with [TestClass] is run automatically when the unit test is invoked.
Add the test project to the solution.
dotnet sln add StringLibraryTest/StringLibraryTest.csproj
Add a project reference
For the test project to work with the StringLibrary
class, add a reference in the StringLibraryTest
project to the StringLibrary
project.
Run the following command:
dotnet add StringLibraryTest/StringLibraryTest.csproj reference StringLibrary/StringLibrary.csproj
Add and run unit test methods
When Visual Studio invokes a unit test, it runs each method that is marked with the TestMethodAttribute attribute in a class that is marked with the TestClassAttribute attribute. A test method ends when the first failure is found or when all tests contained in the method have succeeded.
The most common tests call members of the Assert class. Many assert methods include at least two parameters, one of which is the expected test result and the other of which is the actual test result. Some of the Assert
class's most frequently called methods are shown in the following table:
Assert methods | Function |
---|---|
Assert.AreEqual |
Verifies that two values or objects are equal. The assert fails if the values or objects aren't equal. |
Assert.AreSame |
Verifies that two object variables refer to the same object. The assert fails if the variables refer to different objects. |
Assert.IsFalse |
Verifies that a condition is false . The assert fails if the condition is true . |
Assert.IsNotNull |
Verifies that an object isn't null . The assert fails if the object is null . |
You can also use the Assert.ThrowsException method in a test method to indicate the type of exception it's expected to throw. The test fails if the specified exception isn't thrown.
In testing the StringLibrary.StartsWithUpper
method, you want to provide a number of strings that begin with an uppercase character. You expect the method to return true
in these cases, so you can call the Assert.IsTrue method. Similarly, you want to provide a number of strings that begin with something other than an uppercase character. You expect the method to return false
in these cases, so you can call the Assert.IsFalse method.
Since your library method handles strings, you also want to make sure that it successfully handles an empty string (String.Empty
) and a null
string. An empty string is one that has no characters and whose Length is 0. A null
string is one that hasn't been initialized. You can call StartsWithUpper
directly as a static method and pass a single String argument. Or you can call StartsWithUpper
as an extension method on a string
variable assigned to null
.
You'll define three methods, each of which calls an Assert method for each element in a string array. You'll call a method overload that lets you specify an error message to be displayed in case of test failure. The message identifies the string that caused the failure.
To create the test methods:
Open StringLibraryTest/UnitTest1.cs and replace all of the code with the following code.
using Microsoft.VisualStudio.TestTools.UnitTesting; using UtilityLibraries; namespace StringLibraryTest; [TestClass] public class UnitTest1 { [TestMethod] public void TestStartsWithUpper() { // Tests that we expect to return true. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsTrue(result, string.Format("Expected for '{0}': true; Actual: {1}", word, result)); } } [TestMethod] public void TestDoesNotStartWithUpper() { // Tests that we expect to return false. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word, result)); } } [TestMethod] public void DirectCallWithNullOrEmpty() { // Tests that we expect to return false. string?[] words = { string.Empty, null }; foreach (var word in words) { bool result = StringLibrary.StartsWithUpper(word); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word == null ? "<null>" : word, result)); } } }
The test of uppercase characters in the
TestStartsWithUpper
method includes the Greek capital letter alpha (U+0391) and the Cyrillic capital letter EM (U+041C). The test of lowercase characters in theTestDoesNotStartWithUpper
method includes the Greek small letter alpha (U+03B1) and the Cyrillic small letter Ghe (U+0433).Save your changes.
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that all tests passed.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 3 ms - StringLibraryTest.dll (net7.0)
Handle test failures
If you're doing test-driven development (TDD), you write tests first and they fail the first time you run them. Then you add code to the app that makes the test succeed. For this tutorial, you created the test after writing the app code that it validates, so you haven't seen the test fail. To validate that a test fails when you expect it to fail, add an invalid value to the test input.
Modify the
words
array in theTestDoesNotStartWithUpper
method to include the string "Error".string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " };
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that one test fails, and it provides an error message for the failed test: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Because of the failure, no strings in the array after "Error" were tested.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Failed TestDoesNotStartWithUpper [28 ms] Error Message: Assert.IsFalse failed. Expected for 'Error': false; Actual: True Stack Trace: at StringLibraryTest.UnitTest1.TestDoesNotStartWithUpper() in C:\ClassLibraryProjects\StringLibraryTest\UnitTest1.cs:line 33 Failed! - Failed: 1, Passed: 2, Skipped: 0, Total: 3, Duration: 31 ms - StringLibraryTest.dll (net5.0)
Remove the string "Error" that you added in step 1. Rerun the test and the tests pass.
Test the Release version of the library
Now that the tests have all passed when running the Debug build of the library, run the tests an additional time against the Release build of the library. A number of factors, including compiler optimizations, can sometimes produce different behavior between Debug and Release builds.
Run the tests with the Release build configuration:
dotnet test StringLibraryTest/StringLibraryTest.csproj --configuration Release
The tests pass.
Debug tests
If you're using Visual Studio Code as your IDE, you can use the same process shown in Debug a .NET console application using Visual Studio Code to debug code using your unit test project. Instead of starting the ShowCase app project, open StringLibraryTest/UnitTest1.cs, and select Debug All Tests between lines 7 and 8. If you're unable to find it, press Ctrl+Shift+P to open the command palette and enter Reload Window.
Visual Studio Code starts the test project with the debugger attached. Execution will stop at any breakpoint you've added to the test project or the underlying library code.
Additional resources
Next steps
In this tutorial, you unit tested a class library. You can make the library available to others by publishing it to NuGet as a package. To learn how, follow a NuGet tutorial:
If you publish a library as a NuGet package, others can install and use it. To learn how, follow a NuGet tutorial:
A library doesn't have to be distributed as a package. It can be bundled with a console app that uses it. To learn how to publish a console app, see the earlier tutorial in this series:
This tutorial shows how to automate unit testing by adding a test project to a solution.
Prerequisites
- This tutorial works with the solution that you create in Create a .NET class library using Visual Studio Code.
Create a unit test project
Unit tests provide automated software testing during your development and publishing. The testing framework that you use in this tutorial is MSTest. MSTest is one of three test frameworks you can choose from. The others are xUnit and nUnit.
Start Visual Studio Code.
Open the
ClassLibraryProjects
solution you created in Create a .NET class library using Visual Studio Code.Create a unit test project named "StringLibraryTest".
dotnet new mstest -f net6.0 -o StringLibraryTest
The
-f net6.0
command changes the default target framework tonet6.0
version.The
-o
or--output
command specifies the location to place the generated output.The project template creates a UnitTest1.cs file with the following code:
using Microsoft.VisualStudio.TestTools.UnitTesting; namespace StringLibraryTest { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { } } }
The source code created by the unit test template does the following:
- It imports the Microsoft.VisualStudio.TestTools.UnitTesting namespace, which contains the types used for unit testing.
- It applies the TestClassAttribute attribute to the
UnitTest1
class. - It applies the TestMethodAttribute attribute to define
TestMethod1
.
Each method tagged with [TestMethod] in a test class tagged with [TestClass] is run automatically when the unit test is invoked.
Add the test project to the solution.
dotnet sln add StringLibraryTest/StringLibraryTest.csproj
Add a project reference
For the test project to work with the StringLibrary
class, add a reference in the StringLibraryTest
project to the StringLibrary
project.
Run the following command:
dotnet add StringLibraryTest/StringLibraryTest.csproj reference StringLibrary/StringLibrary.csproj
Add and run unit test methods
When Visual Studio invokes a unit test, it runs each method that is marked with the TestMethodAttribute attribute in a class that is marked with the TestClassAttribute attribute. A test method ends when the first failure is found or when all tests contained in the method have succeeded.
The most common tests call members of the Assert class. Many assert methods include at least two parameters, one of which is the expected test result and the other of which is the actual test result. Some of the Assert
class's most frequently called methods are shown in the following table:
Assert methods | Function |
---|---|
Assert.AreEqual |
Verifies that two values or objects are equal. The assert fails if the values or objects aren't equal. |
Assert.AreSame |
Verifies that two object variables refer to the same object. The assert fails if the variables refer to different objects. |
Assert.IsFalse |
Verifies that a condition is false . The assert fails if the condition is true . |
Assert.IsNotNull |
Verifies that an object isn't null . The assert fails if the object is null . |
You can also use the Assert.ThrowsException method in a test method to indicate the type of exception it's expected to throw. The test fails if the specified exception isn't thrown.
In testing the StringLibrary.StartsWithUpper
method, you want to provide a number of strings that begin with an uppercase character. You expect the method to return true
in these cases, so you can call the Assert.IsTrue method. Similarly, you want to provide a number of strings that begin with something other than an uppercase character. You expect the method to return false
in these cases, so you can call the Assert.IsFalse method.
Since your library method handles strings, you also want to make sure that it successfully handles an empty string (String.Empty
) and a null
string. An empty string is one that has no characters and whose Length is 0. A null
string is one that hasn't been initialized. You can call StartsWithUpper
directly as a static method and pass a single String argument. Or you can call StartsWithUpper
as an extension method on a string
variable assigned to null
.
You'll define three methods, each of which calls an Assert method for each element in a string array. You'll call a method overload that lets you specify an error message to be displayed in case of test failure. The message identifies the string that caused the failure.
To create the test methods:
Open StringLibraryTest/UnitTest1.cs and replace all of the code with the following code.
using Microsoft.VisualStudio.TestTools.UnitTesting; using UtilityLibraries; namespace StringLibraryTest; [TestClass] public class UnitTest1 { [TestMethod] public void TestStartsWithUpper() { // Tests that we expect to return true. string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsTrue(result, string.Format("Expected for '{0}': true; Actual: {1}", word, result)); } } [TestMethod] public void TestDoesNotStartWithUpper() { // Tests that we expect to return false. string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " }; foreach (var word in words) { bool result = word.StartsWithUpper(); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word, result)); } } [TestMethod] public void DirectCallWithNullOrEmpty() { // Tests that we expect to return false. string?[] words = { string.Empty, null }; foreach (var word in words) { bool result = StringLibrary.StartsWithUpper(word); Assert.IsFalse(result, string.Format("Expected for '{0}': false; Actual: {1}", word == null ? "<null>" : word, result)); } } }
The test of uppercase characters in the
TestStartsWithUpper
method includes the Greek capital letter alpha (U+0391) and the Cyrillic capital letter EM (U+041C). The test of lowercase characters in theTestDoesNotStartWithUpper
method includes the Greek small letter alpha (U+03B1) and the Cyrillic small letter Ghe (U+0433).Save your changes.
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that all tests passed.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 3 ms - StringLibraryTest.dll (net6.0)
Handle test failures
If you're doing test-driven development (TDD), you write tests first and they fail the first time you run them. Then you add code to the app that makes the test succeed. For this tutorial, you created the test after writing the app code that it validates, so you haven't seen the test fail. To validate that a test fails when you expect it to fail, add an invalid value to the test input.
Modify the
words
array in theTestDoesNotStartWithUpper
method to include the string "Error".string[] words = { "alphabet", "Error", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство", "1234", ".", ";", " " };
Run the tests:
dotnet test StringLibraryTest/StringLibraryTest.csproj
The terminal output shows that one test fails, and it provides an error message for the failed test: "Assert.IsFalse failed. Expected for 'Error': false; actual: True". Because of the failure, no strings in the array after "Error" were tested.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Failed TestDoesNotStartWithUpper [28 ms] Error Message: Assert.IsFalse failed. Expected for 'Error': false; Actual: True Stack Trace: at StringLibraryTest.UnitTest1.TestDoesNotStartWithUpper() in C:\ClassLibraryProjects\StringLibraryTest\UnitTest1.cs:line 33 Failed! - Failed: 1, Passed: 2, Skipped: 0, Total: 3, Duration: 31 ms - StringLibraryTest.dll (net5.0)
Remove the string "Error" that you added in step 1. Rerun the test and the tests pass.
Test the Release version of the library
Now that the tests have all passed when running the Debug build of the library, run the tests an additional time against the Release build of the library. A number of factors, including compiler optimizations, can sometimes produce different behavior between Debug and Release builds.
Run the tests with the Release build configuration:
dotnet test StringLibraryTest/StringLibraryTest.csproj --configuration Release
The tests pass.
Debug tests
If you're using Visual Studio Code as your IDE, you can use the same process shown in Debug a .NET console application using Visual Studio Code to debug code using your unit test project. Instead of starting the ShowCase app project, open StringLibraryTest/UnitTest1.cs, and select Debug All Tests between lines 7 and 8. If you're unable to find it, press Ctrl+Shift+P to open the command palette and enter Reload Window.
Visual Studio Code starts the test project with the debugger attached. Execution will stop at any breakpoint you've added to the test project or the underlying library code.
Additional resources
Next steps
In this tutorial, you unit tested a class library. You can make the library available to others by publishing it to NuGet as a package. To learn how, follow a NuGet tutorial:
If you publish a library as a NuGet package, others can install and use it. To learn how, follow a NuGet tutorial:
A library doesn't have to be distributed as a package. It can be bundled with a console app that uses it. To learn how to publish a console app, see the earlier tutorial in this series:
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for