Skip to content

Commit 51299d5

Browse files
authored
Add DataRow attribute allowing to have test cases in test methods using attributes (#158)
1 parent 54cb5cd commit 51299d5

File tree

9 files changed

+160
-20
lines changed

9 files changed

+160
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
using System;
8+
using System.Diagnostics;
9+
using System.Threading;
10+
11+
namespace nanoFramework.TestFramework.Test
12+
{
13+
[TestClass]
14+
public class TestOfDataRow
15+
{
16+
[DataRow(1, 2, 3)]
17+
[DataRow(5, 6, 11)]
18+
public void TestAddition(int number1, int number2, int result)
19+
{
20+
var additionResult = number1 + number2;
21+
22+
Assert.Equal(additionResult, result);
23+
}
24+
25+
[DataRow("TestString")]
26+
public void TestString(string testData)
27+
{
28+
Assert.Equal(testData, "TestString");
29+
}
30+
}
31+
}

poc/TestOfTestFrameworkByReference/NFUnitTestByReference.nfproj

+5-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<RunSettingsFilePath>$(MSBuildProjectDirectory)\nano.runsettings</RunSettingsFilePath>
2828
</PropertyGroup>
2929
<ItemGroup>
30+
<Compile Include="DataRowTests.cs" />
3031
<Compile Include="SkipFewMethods.cs" />
3132
<Compile Include="SkipTestClass.cs" />
3233
<Compile Include="Test.cs" />
@@ -41,13 +42,11 @@
4142
<None Include="packages.config" />
4243
</ItemGroup>
4344
<ItemGroup>
44-
<Reference Include="mscorlib, Version=1.11.7.2, Culture=neutral, PublicKeyToken=c07d481e9758c731">
45-
<HintPath>..\packages\nanoFramework.CoreLibrary.1.11.7\lib\mscorlib.dll</HintPath>
46-
<Private>True</Private>
45+
<Reference Include="mscorlib">
46+
<HintPath>..\packages\nanoFramework.CoreLibrary.1.12.0\lib\mscorlib.dll</HintPath>
4747
</Reference>
48-
<Reference Include="nanoFramework.Runtime.Native, Version=1.5.2.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
49-
<HintPath>..\packages\nanoFramework.Runtime.Native.1.5.2-preview.6\lib\nanoFramework.Runtime.Native.dll</HintPath>
50-
<Private>True</Private>
48+
<Reference Include="nanoFramework.Runtime.Native">
49+
<HintPath>..\packages\nanoFramework.Runtime.Native.1.5.4\lib\nanoFramework.Runtime.Native.dll</HintPath>
5150
</Reference>
5251
</ItemGroup>
5352
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.CSharp.targets')" />
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="nanoFramework.CoreLibrary" version="1.11.7" targetFramework="netnanoframework10" />
4-
<package id="nanoFramework.Runtime.Native" version="1.5.2-preview.6" targetFramework="netnanoframework10" />
3+
<package id="nanoFramework.CoreLibrary" version="1.12.0" targetFramework="netnanoframework10" />
4+
<package id="nanoFramework.Runtime.Native" version="1.5.4" targetFramework="netnanoframework10" />
55
</packages>

source/TestAdapter/Discover.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,10 @@ public static List<TestCase> FindTestCases(string source)
137137
{
138138
if (attrib.GetType().FullName == typeof(SetupAttribute).FullName ||
139139
attrib.GetType().FullName == typeof(TestMethodAttribute).FullName ||
140-
attrib.GetType().FullName == typeof(CleanupAttribute).FullName)
140+
attrib.GetType().FullName == typeof(CleanupAttribute).FullName ||
141+
attrib.GetType().FullName == typeof(DataRowAttribute).FullName)
141142
{
142-
var testCase = GetFileNameAndLineNumber(allCsFils, type, method);
143+
var testCase = GetFileNameAndLineNumber(allCsFils, type, method, attrib);
143144
testCase.Source = source;
144145
testCase.ExecutorUri = new Uri(TestsConstants.NanoExecutor);
145146
testCase.FullyQualifiedName = $"{type.FullName}.{testCase.DisplayName}";
@@ -221,7 +222,7 @@ private static FileInfo[] FindNfprojSources(string source)
221222
}
222223
}
223224

224-
private static TestCase GetFileNameAndLineNumber(string[] csFiles, Type className, MethodInfo method)
225+
private static TestCase GetFileNameAndLineNumber(string[] csFiles, Type className, MethodInfo method, object attribute)
225226
{
226227
var clName = className.Name;
227228
var methodName = method.Name;
@@ -242,7 +243,7 @@ private static TestCase GetFileNameAndLineNumber(string[] csFiles, Type classNam
242243
{
243244
flret.CodeFilePath = csFile;
244245
flret.LineNumber = lineNum;
245-
flret.DisplayName = method.Name;
246+
flret.DisplayName = Helper.GetTestDisplayName(method, attribute);
246247
return flret;
247248
}
248249

source/TestAdapter/Executor.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -735,9 +735,8 @@ private void CheckAllTests(string rawOutput, List<TestResult> results)
735735
if (line.Contains(TestPassed))
736736
{
737737
// Format is "Test passed: MethodName, ticks";
738-
// We do get split with space if the coma is missing, happens time to time
739738

740-
string method = line.Substring(line.IndexOf(TestPassed) + TestPassed.Length).Split(',')[0].Split(' ')[0];
739+
string method = line.Substring(line.IndexOf(TestPassed) + TestPassed.Length).Split(',')[0];
741740
string ticks = line.Substring(line.IndexOf(TestPassed) + TestPassed.Length + method.Length + 2);
742741

743742
long ticksNum = 0;
@@ -761,7 +760,7 @@ private void CheckAllTests(string rawOutput, List<TestResult> results)
761760
{
762761
// Format is "Test failed: MethodName, Exception message";
763762

764-
string method = line.Substring(line.IndexOf(TestFailed) + TestFailed.Length).Split(',')[0].Split(' ')[0];
763+
string method = line.Substring(line.IndexOf(TestFailed) + TestFailed.Length).Split(',')[0];
765764

766765
string exception = line.Substring(line.IndexOf(TestFailed) + TestPassed.Length + method.Length + 2);
767766

@@ -783,7 +782,7 @@ private void CheckAllTests(string rawOutput, List<TestResult> results)
783782
{
784783
// Format is "Test failed: MethodName, Exception message";
785784

786-
string method = line.Substring(line.IndexOf(TestSkipped) + TestSkipped.Length).Split(',')[0].Split(' ')[0];
785+
string method = line.Substring(line.IndexOf(TestSkipped) + TestSkipped.Length).Split(',')[0];
787786

788787
string exception = line.Substring(line.IndexOf(TestSkipped) + TestPassed.Length + method.Length + 2);
789788

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
using System;
8+
9+
namespace nanoFramework.TestFramework
10+
{
11+
/// <summary>
12+
/// Data row attribute. Used for passing multiple parameters into same test method.
13+
/// </summary>
14+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
15+
public class DataRowAttribute : Attribute
16+
{
17+
/// <summary>
18+
/// Array containing all passed parameters
19+
/// </summary>
20+
public object[] MethodParameters { get; }
21+
22+
/// <summary>
23+
/// Initializes a new instance of the DataRowAttribute class.
24+
/// </summary>
25+
/// <param name="methodParameters">Parameters which should be stored for future execution of test method</param>
26+
/// <exception cref="ArgumentNullException">Thrown when methodParameters is null</exception>
27+
/// <exception cref="ArgumentException">Thrown when methodParameters is empty</exception>
28+
public DataRowAttribute(params object[] methodParameters)
29+
{
30+
if (methodParameters == null)
31+
{
32+
throw new ArgumentNullException($"{nameof(methodParameters)} can not be null");
33+
}
34+
35+
if (methodParameters.Length == 0)
36+
{
37+
throw new ArgumentException($"{nameof(methodParameters)} can not be empty");
38+
}
39+
40+
MethodParameters = methodParameters;
41+
}
42+
}
43+
}

source/TestFrameworkShared/Helper.cs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
4+
// See LICENSE file in the project root for full license information.
5+
//
6+
7+
using System.Reflection;
8+
9+
namespace nanoFramework.TestFramework
10+
{
11+
/// <summary>
12+
/// Helper class for keeping test name same in TestAdapter and TestRunner
13+
/// </summary>
14+
public static class Helper
15+
{
16+
private static string GetJoinedParams(object[] data)
17+
{
18+
var returnString = string.Empty;
19+
foreach (var item in data)
20+
{
21+
returnString += $"{item} | ";
22+
}
23+
24+
// In each loop iteration we are appending " | " event at the end
25+
// To keep return string clean, we are removing last 3 charcters
26+
// Lenght starts from 1, substring from 0
27+
// To remove 3 last characters using this method, we need to add 1
28+
return returnString.Substring(0, returnString.Length - 4);
29+
}
30+
31+
/// <summary>
32+
/// Generates test display name based on passed <paramref name="method"/> and <paramref name="attribute"/>.
33+
/// </summary>
34+
/// <returns>Returns method name with parameters if passed attribute is of DataRow type</returns>
35+
public static string GetTestDisplayName(MethodInfo method, object attribute)
36+
{
37+
// Comparing via full name, because attribute parameter is from "TestFramework.dll"
38+
// and current type TestCaseAttribute is in scope of "TestAdapter" due to shared project
39+
// The same reason - reflection to get value
40+
if (attribute.GetType().FullName == typeof(DataRowAttribute).FullName)
41+
{
42+
var methodParameters = (object[])attribute.GetType()
43+
.GetMethod($"get_{nameof(DataRowAttribute.MethodParameters)}").Invoke(attribute, null);
44+
45+
return $"{method.Name} - (params: {GetJoinedParams(methodParameters)})";
46+
}
47+
48+
return method.Name;
49+
}
50+
}
51+
}

source/TestFrameworkShared/TestFrameworkShared.projitems

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
</PropertyGroup>
1111
<ItemGroup>
1212
<Compile Include="$(MSBuildThisFileDirectory)CleanupAttribute.cs" />
13+
<Compile Include="$(MSBuildThisFileDirectory)DataRowAttribute.cs" />
14+
<Compile Include="$(MSBuildThisFileDirectory)Helper.cs" />
1315
<Compile Include="$(MSBuildThisFileDirectory)SetupAttribute.cs" />
1416
<Compile Include="$(MSBuildThisFileDirectory)SkipTestException.cs" />
1517
<Compile Include="$(MSBuildThisFileDirectory)TestClassAttribute.cs" />

source/UnitTestLauncher/Program.cs

+18-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public static void Main()
4343
{
4444
// then we run the tests
4545
RunTest(methods, typeof(TestMethodAttribute));
46+
RunTest(methods, typeof(DataRowAttribute));
4647

4748
// last we handle Cleanup
4849
RunTest(methods, typeof(CleanupAttribute));
@@ -67,21 +68,23 @@ private static bool RunTest(
6768

6869
foreach (var attrib in attribs)
6970
{
71+
var methodName = Helper.GetTestDisplayName(method, attrib);
7072
if (attribToRun == attrib.GetType())
7173
{
7274
try
7375
{
7476
dt = DateTime.UtcNow.Ticks;
75-
method.Invoke(null, null);
77+
object[] parameters = GetParameters(attrib);
78+
method.Invoke(null, parameters);
7679
totalTicks = DateTime.UtcNow.Ticks - dt;
7780

78-
Console.WriteLine($"Test passed: {method.Name}, {totalTicks}");
81+
Console.WriteLine($"Test passed: {methodName}, {totalTicks}");
7982
}
8083
catch (Exception ex)
8184
{
8285
if (ex.GetType() == typeof(SkipTestException))
8386
{
84-
Console.WriteLine($"Test skipped: {method.Name}, {ex.Message}");
87+
Console.WriteLine($"Test skipped: {methodName}, {ex.Message}");
8588
if (isSetupMethod)
8689
{
8790
// In case the Setup attribute test is skipped, we will skip
@@ -91,7 +94,7 @@ private static bool RunTest(
9194
}
9295
else
9396
{
94-
Console.WriteLine($"Test failed: {method.Name}, {ex.Message}");
97+
Console.WriteLine($"Test failed: {methodName}, {ex.Message}");
9598
}
9699
}
97100

@@ -101,5 +104,16 @@ private static bool RunTest(
101104

102105
return true;
103106
}
107+
108+
private static object[] GetParameters(object attribute)
109+
{
110+
if (attribute.GetType() != typeof(DataRowAttribute))
111+
{
112+
return null;
113+
}
114+
115+
var testCaseAttribute = (DataRowAttribute)attribute;
116+
return testCaseAttribute.MethodParameters;
117+
}
104118
}
105119
}

0 commit comments

Comments
 (0)