Skip to content

Commit ecb3a6e

Browse files
authored
Merge pull request #8564 from Evangelink/mstest-data-driven
Update doc for MSTest data-driven tests
2 parents 7e0f782 + 3722348 commit ecb3a6e

File tree

2 files changed

+99
-71
lines changed

2 files changed

+99
-71
lines changed

docs/test/how-to-create-a-data-driven-unit-test.md

Lines changed: 96 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Create Data-Driven Unit Tests
33
description: Learn how to use the Microsoft unit test framework for managed code to set up a unit test method to retrieve values from a data source.
44
ms.custom: SEO-VS-2020
5-
ms.date: 05/08/2019
5+
ms.date: 10/17/2012
66
ms.topic: how-to
77
f1_keywords:
88
- vs.test.testresults.unittest.datadriven
@@ -22,17 +22,13 @@ author: mikejo5000
2222

2323
[!INCLUDE [Visual Studio](~/includes/applies-to-version/vs-windows-only.md)]
2424

25-
You can use the Microsoft unit test framework for managed code to set up a unit test method to retrieve values from a data source. The method is run successively for each row in the data source, which makes it easy to test a variety of input by using a single method.
25+
You can use the Microsoft unit test framework (MSTest) for managed code to set up a unit test method to retrieve values from a data source. The method is run successively for each row in the data source, which makes it easy to test a variety of input by using a single method.
2626

27-
Creating a data-driven unit test involves the following steps:
27+
A data-driven unit test can use any of the following kind:
2828

29-
1. Create a data source that contains the values that you use in the test method. The data source can be any type that is registered on the machine that runs the test.
30-
31-
2. Add a private <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> field and a public `TestContext` property to the test class.
32-
33-
3. Create a unit test method and add a <xref:Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute> attribute to it.
34-
35-
4. Use the <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DataRow%2A> indexer property to retrieve the values that you use in a test.
29+
- inline data using the `DataRow` attribute
30+
- member data using the `DynamicData` attribute
31+
- from some well-known source provider using the `DataSource` attribute
3632

3733
## The method under test
3834

@@ -54,15 +50,94 @@ We'll test a method in `Maths` that adds two integers using a loop:
5450
public int AddIntegers(int first, int second)
5551
{
5652
int sum = first;
57-
for( int i = 0; i < second; i++)
53+
for (int i = 0; i < second; i++)
5854
{
5955
sum += 1;
6056
}
57+
6158
return sum;
6259
}
6360
```
6461

65-
## Create a data source
62+
## Test test method
63+
64+
### Inline data-driven test
65+
66+
For inline tests, MSTest uses `DataRow` to specify values used by the data-driven test. The test in this example runs successively for each data row.
67+
68+
```csharp
69+
[DataTestMethod]
70+
[DataRow(1, 1, 2)]
71+
[DataRow(2, 2, 4)]
72+
[DataRow(3, 3, 6)]
73+
[DataRow(0, 0, 1)] // The test run with this row fails
74+
public void AddIntegers_FromDataRowTest(int x, int y, int expected)
75+
{
76+
var target = new Maths();
77+
int actual = target.AddIntegers(x, y);
78+
Assert.AreEqual(expected, actual,
79+
"x:<{0}> y:<{1}>",
80+
new object[] {x, y});
81+
}
82+
```
83+
84+
### Member data-driven test
85+
86+
MSTest uses `DynamicData` attribute to specify the name, kind (property, the default, or method) and defining type (by default current type is used) of the member that will provide the data used by the data-driven test.
87+
88+
```csharp
89+
public static IEnumerable<object[]> AdditionData
90+
{
91+
get
92+
{
93+
return new[]
94+
{
95+
new object[] { 1, 1, 2 },
96+
new object[] { 2, 2, 4 },
97+
new object[] { 3, 3, 6 },
98+
new object[] { 0, 0, 1 }, // The test run with this row fails
99+
};
100+
}
101+
}
102+
103+
[TestMethod]
104+
[DynamicData(nameof(ReusableTestDataProperty))]
105+
public void AddIntegers_FromDynamicDataTest(int x, int y, int expected)
106+
{
107+
var target = new Maths();
108+
int actual = target.AddIntegers(x, y);
109+
Assert.AreEqual(expected, actual,
110+
"x:<{0}> y:<{1}>",
111+
new object[] {x, y});
112+
}
113+
```
114+
115+
It is also possible to override the default generated display name, using the `DynamicDataDisplayName` property of the `DynamicData` attribute. The display name method signature must be `publc static string` and accept two parameters, the first of type `MethodInfo` and the second of type `object[]`.
116+
117+
```csharp
118+
public static string GetCustomDynamicDataDisplayName(MethodInfo methodInfo, object[] data)
119+
{
120+
return string.Format("DynamicDataTestMethod {0} with {1} parameters", methodInfo.Name, data.Length);
121+
}
122+
123+
[DynamicData(nameof(AdditionData), DynamicDataDisplayName = nameof(GetCustomDynamicDataDisplayName))]
124+
```
125+
126+
### Source provider data-driven test
127+
128+
Creating a data source driven unit test involves the following steps:
129+
130+
1. Create a data source that contains the values that you use in the test method. The data source can be any type that is registered on the machine that runs the test.
131+
132+
2. Add a public `TestContext` property of type <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext> to the test class.
133+
134+
3. Create a unit test method
135+
136+
4. Add a <xref:Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute> attribute to it.
137+
138+
5. Use the <xref:Microsoft.VisualStudio.TestTools.UnitTesting.TestContext.DataRow%2A> indexer property to retrieve the values that you use in a test.
139+
140+
#### Create a data source
66141

67142
To test the `AddIntegers` method, create a data source that specifies a range of values for the parameters and the sum that you expect to be returned. In this example, we'll create a Sql Compact database named `MathsData` and a table named `AddIntegersData` that contains the following column names and values
68143

@@ -72,31 +147,26 @@ To test the `AddIntegers` method, create a data source that specifies a range of
72147
|1|1|2|
73148
|2|-3|-1|
74149

75-
## Add a TestContext to the test class
150+
#### Add a TestContext to the test class
76151

77152
The unit test framework creates a `TestContext` object to store the data source information for a data-driven test. The framework then sets this object as the value of the `TestContext` property that you create.
78153

79154
```csharp
80-
private TestContext testContextInstance;
81-
public TestContext TestContext
82-
{
83-
get { return testContextInstance; }
84-
set { testContextInstance = value; }
85-
}
155+
public TestContext TestContext { get; set; }
86156
```
87157

88158
In your test method, you access the data through the `DataRow` indexer property of the `TestContext`.
89159

90160
> [!NOTE]
91-
> .NET Core does not support the [DataSource](xref:Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute) attribute. If you try to access test data in this way in a .NET Core or UWP unit test project, you'll see an error similar to **"'TestContext' does not contain a definition for 'DataRow' and no accessible extension method 'DataRow' accepting a first argument of type 'TestContext' could be found (are you missing a using directive or an assembly reference?)"**.
161+
> .NET Core does not support the [DataSource](xref:Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute) attribute. If you try to access test data in this way in a .NET Core, UWP or WinUI unit test project, you will see an error similar to **"'TestContext' does not contain a definition for 'DataRow' and no accessible extension method 'DataRow' accepting a first argument of type 'TestContext' could be found (are you missing a using directive or an assembly reference?)"**.
92162
93-
## Write the test method
163+
#### Write the test method
94164

95165
The test method for `AddIntegers` is fairly simple. For each row in the data source, call `AddIntegers` with the **FirstNumber** and **SecondNumber** column values as parameters, and verify the return value against **Sum** column value:
96166

97167
```csharp
168+
[TestMethod]
98169
[DataSource(@"Provider=Microsoft.SqlServerCe.Client.4.0; Data Source=C:\Data\MathsData.sdf;", "Numbers")]
99-
[TestMethod()]
100170
public void AddIntegers_FromDataSourceTest()
101171
{
102172
var target = new Maths();
@@ -105,16 +175,14 @@ public void AddIntegers_FromDataSourceTest()
105175
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
106176
int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
107177
int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
108-
int actual = target.IntegerMethod(x, y);
178+
int actual = target.AddIntegers(x, y);
109179
Assert.AreEqual(expected, actual,
110180
"x:<{0}> y:<{1}>",
111181
new object[] {x, y});
112182
}
113183
```
114184

115-
The `Assert` method includes a message that displays the `x` and `y` values of a failed iteration. By default, the asserted values - `expected` and `actual` - are already included in failed test details.
116-
117-
### Specify the DataSourceAttribute
185+
#### Specify the DataSourceAttribute
118186

119187
The `DataSource` attribute specifies the connection string for the data source and the name of the table that you use in the test method. The exact information in the connection string differs, depending on what kind of data source you are using. In this example, we used a SqlServerCe database.
120188

@@ -149,7 +217,7 @@ The connection strings depend on the type of the type of data source, but it sho
149217
)]
150218
```
151219

152-
### Use TestContext.DataRow to access the data
220+
#### Use TestContext.DataRow to access the data
153221

154222
To access the data in the `AddIntegersData` table, use the `TestContext.DataRow` indexer. `DataRow` is a <xref:System.Data.DataRow> object, so retrieve column values by index or column names. Because the values are returned as objects, convert them to the appropriate type:
155223

@@ -166,26 +234,10 @@ The test results bar at the top of **Test Explorer** is animated as your test ru
166234
> [!NOTE]
167235
> There's a result for each row of data and also one summary result. If the test passed on each row of data, the summary run shows as **Passed**. If the test failed on any data row, the summary run shows as **Failed**.
168236
169-
If you ran the `AddIntegers_FromDataSourceTest` method in our example, the results bar turns red and the test method is moved to the **Failed Tests**. A data-driven test fails if any of the iterated methods from the data source fails. When you choose a failed data-driven test in the **Test Explorer** window, the details pane displays the results of each iteration that is identified by the data row index. In our example, it appears that the `AddIntegers` algorithm does not handle negative values correctly.
237+
If you ran any of the `AddIntegers_FromDataRowTest`, `AddIntegers_FromDynamicDataTest` or `AddIntegers_FromDataSourceTest` method in our example, the results bar turns red and the test method is moved to the **Failed Tests**. A data-driven test fails if any of the iterated methods from the data source fails. When you choose a failed data-driven test in the **Test Explorer** window, the details pane displays the results of each iteration that is identified by the data row index. In our example, it appears that the `AddIntegers` algorithm does not handle negative values correctly.
170238

171239
When the method under test is corrected and the test rerun, the results bar turns green and the test method is moved to the **Passed Test** group.
172240

173-
## Run an inline data-driven test
174-
175-
For inline tests, MSTest uses `DataRow` to retrieve values from a data source. The test in this example runs successively for each data row.
176-
177-
```csharp
178-
[DataTestMethod]
179-
[DataRow(1, 1, 2)]
180-
[DataRow(2, 2, 4)]
181-
[DataRow(3, 3, 6)]
182-
[DataRow(0, 0, 1)] // The test run with this row fails
183-
public void AddTests(int x, int y, int expected)
184-
{
185-
Assert.AreEqual(expected, x + y);
186-
}
187-
```
188-
189241
## See also
190242

191243
- <xref:Microsoft.VisualStudio.TestTools.UnitTesting.DataSourceAttribute?displayProperty=fullName>

docs/test/unit-test-basics.md

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Unit testing fundamentals
33
description: Learn how Visual Studio Test Explorer provides a flexible and efficient way to run your unit tests and view their results.
4-
ms.date: 12/28/2021
4+
ms.date: 10/17/2021
55
ms.topic: conceptual
66
f1_keywords:
77
- vs.UnitTest.CreateUnitTest
@@ -298,33 +298,9 @@ Learn more details about [debugging unit tests](../debugger/debugger-feature-tou
298298

299299
**Q: Can I create unit tests that take multiple sets of data as input to run the test?**
300300

301-
**A:** Yes. *Data-driven test methods* let you test a range of values with a single unit test method. Use a `DataSource` attribute for the test method that specifies the data source and table that contains the variable values that you want to test. In the method body, you assign the row values to variables using the `TestContext.DataRow[`*ColumnName*`]` indexer.
301+
**A:** Yes. *Data-driven test methods* let you test a range of values with a single unit test method. Use a `DataRow`, `DynamicData` or `DataSource` attribute for the test method that specifies the data source that contains the variable values that you want to test.
302302

303-
> [!NOTE]
304-
> These procedures apply only to test methods that you write by using the Microsoft unit test framework for managed code. If you're using a different framework, consult the framework documentation for equivalent functionality.
305-
306-
For example, assume we add an unnecessary method to the `CheckingAccount` class that is named `AddIntegerHelper`. `AddIntegerHelper` adds two integers.
307-
308-
To create a data-driven test for the `AddIntegerHelper` method, we first create an Access database named *AccountsTest.accdb* and a table named `AddIntegerHelperData`. The `AddIntegerHelperData` table defines columns to specify the first and second operands of the addition and a column to specify the expected result. We fill a number of rows with appropriate values.
309-
310-
```csharp
311-
[DataSource(
312-
@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Projects\MyBank\TestData\AccountsTest.accdb",
313-
"AddIntegerHelperData"
314-
)]
315-
[TestMethod()]
316-
public void AddIntegerHelper_DataDrivenValues_AllShouldPass()
317-
{
318-
var target = new CheckingAccount();
319-
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
320-
int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
321-
int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
322-
int actual = target.AddIntegerHelper(x, y);
323-
Assert.AreEqual(expected, actual);
324-
}
325-
```
326-
327-
The attributed method runs once for each row in the table. **Test Explorer** reports a test failure for the method if any of the iterations fail. The test results detail pane for the method shows you the pass/fail status method for each row of data.
303+
The attributed method runs once for each row in the data source. **Test Explorer** reports a test failure for the method if any of the iterations fail. The test results detail pane for the method shows you the pass/fail status method for each row of data.
328304

329305
Learn more about [data-driven unit tests](../test/how-to-create-a-data-driven-unit-test.md).
330306

0 commit comments

Comments
 (0)