Skip to content
Merged
39 changes: 27 additions & 12 deletions src/Adapter/MSTest.CoreAdapter/Execution/TestMethodRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,22 +247,30 @@ internal UnitTestResult[] RunTestMethod()
watch.Start();

this.testContext.SetDataRow(dataRow);
UTF.TestResult currentResult;
UTF.TestResult[] testResults;

try
{
currentResult = this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo)[0];
testResults = this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo);
}
catch (Exception ex)
{
currentResult = new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) };
testResults = new[]
{
new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) }
};
}

currentResult.DatarowIndex = rowIndex++;
watch.Stop();
currentResult.Duration = watch.Elapsed;
foreach (var testResult in testResults)
{
testResult.DatarowIndex = rowIndex;
testResult.Duration = watch.Elapsed;
}

results.Add(currentResult);
rowIndex++;

results.AddRange(testResults);
}
}
finally
Expand Down Expand Up @@ -293,18 +301,25 @@ internal UnitTestResult[] RunTestMethod()
foreach (var data in testDataSource.GetData(this.testMethodInfo.MethodInfo))
{
this.testMethodInfo.SetArguments(data);
UTF.TestResult currentResult;
UTF.TestResult[] testResults;
try
{
currentResult = this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo)[0];
testResults = this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo);
}
catch (Exception ex)
{
currentResult = new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) };
testResults = new[]
{
new UTF.TestResult() { TestFailureException = new Exception(string.Format(CultureInfo.CurrentCulture, Resource.UTA_ExecuteThrewException, ex.Message), ex) }
};
}

foreach (var testResult in testResults)
{
testResult.DisplayName = testDataSource.GetDisplayName(this.testMethodInfo.MethodInfo, data);
}

currentResult.DisplayName = testDataSource.GetDisplayName(this.testMethodInfo.MethodInfo, data);
results.Add(currentResult);
results.AddRange(testResults);
this.testMethodInfo.SetArguments(null);
}
}
Expand All @@ -313,7 +328,7 @@ internal UnitTestResult[] RunTestMethod()
{
try
{
results.Add(this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo)[0]);
results.AddRange(this.testMethodInfo.TestMethodOptions.Executor.Execute(this.testMethodInfo));
}
catch (Exception ex)
{
Expand Down
6 changes: 4 additions & 2 deletions test/E2ETests/Automation.CLI/CLITestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Microsoft.MSTestV2.CLIAutomation
using System.Linq;
using System.Xml;
using Microsoft.TestPlatform.VsTestConsole.TranslationLayer;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestTools.UnitTesting;

public class CLITestBase
Expand Down Expand Up @@ -59,7 +60,8 @@ public void InvokeVsTestForDiscovery(string[] sources, string runSettings = "")
/// </summary>
/// <param name="sources">List of test assemblies.</param>
/// <param name="runSettings">Run settings for execution.</param>
public void InvokeVsTestForExecution(string[] sources, string runSettings = "")
/// <param name="testCaseFilter">Test Case filter for execution.</param>
public void InvokeVsTestForExecution(string[] sources, string runSettings = "", string testCaseFilter = null)
{
for (var iterator = 0; iterator < sources.Length; iterator++)
{
Expand All @@ -74,7 +76,7 @@ public void InvokeVsTestForExecution(string[] sources, string runSettings = "")

// this step of Initializing extensions should not be required after this issue: https://github.com/Microsoft/vstest/issues/236 is fixed
vsTestConsoleWrapper.InitializeExtensions(Directory.GetFiles(this.GetTestAdapterPath(), "*TestAdapter.dll"));
vsTestConsoleWrapper.RunTests(sources, runSettingXml, this.runEventsHandler);
vsTestConsoleWrapper.RunTests(sources, runSettingXml, new TestPlatformOptions { TestCaseFilter = testCaseFilter }, this.runEventsHandler);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace MSTestAdapter.Smoke.E2ETests
{
using Microsoft.MSTestV2.CLIAutomation;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CustomTestExecutionExtensibilityTests : CLITestBase
{
private const string TestAssembly = "FxExtensibilityTestProject.dll";

[TestMethod]
public void ExecuteCustomTestExtensibilityTests()
{
this.InvokeVsTestForExecution(new string[] { TestAssembly });
this.ValidatePassedTestsContain(
"CustomTestMethod1 - Execution number 1",
"CustomTestMethod1 - Execution number 2",
"CustomTestMethod1 - Execution number 4",
"CustomTestMethod1 - Execution number 5",
"CustomTestClass1 - Execution number 1",
"CustomTestClass1 - Execution number 2",
"CustomTestClass1 - Execution number 4",
"CustomTestClass1 - Execution number 5");
this.ValidateFailedTestsContain(
TestAssembly,
"CustomTestMethod1 - Execution number 3",
"CustomTestClass1 - Execution number 3");
}

[TestMethod]
public void ExecuteCustomTestExtensibilityWithTestDataTests()
{
this.InvokeVsTestForExecution(new string[] { TestAssembly }, testCaseFilter: "FullyQualifiedName~CustomTestExTests.CustomTestMethod2");
this.ValidatePassedTests(
"CustomTestMethod2 (B)",
"CustomTestMethod2 (B)",
"CustomTestMethod2 (B)");
this.ValidateFailedTests(
TestAssembly,
"CustomTestMethod2 (A)",
"CustomTestMethod2 (A)",
"CustomTestMethod2 (A)",
"CustomTestMethod2 (C)",
"CustomTestMethod2 (C)",
"CustomTestMethod2 (C)");
}
}
}
1 change: 1 addition & 0 deletions test/E2ETests/Smoke.E2E.Tests/Smoke.E2E.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssertExtensibilityTests.cs" />
<Compile Include="CustomTestExecutionExtensibilityTests.cs" />
<Compile Include="DataSourceTests.cs" />
<Compile Include="DesktopCSharpCLITests.cs" />
<Compile Include="ParallelExecutionTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
namespace FxExtensibilityTestProject
{
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[IterativeTestClass(5)]
public class CustomTestExTests
{
private static int customTestMethod1ExecutionCount;
[IterativeTestMethod(5)]
public void CustomTestMethod1()
{
customTestMethod1ExecutionCount++;
Assert.AreNotEqual(3, customTestMethod1ExecutionCount);
}

[IterativeTestMethod(3)]
[DataRow("A")]
[DataRow("B")]
[DataRow("C")]
public void CustomTestMethod2(string value)
{
Assert.AreEqual("B", value);
}

private static int customTestClass1ExecutionCount;
[TestMethod]
public void CustomTestClass1()
{
customTestClass1ExecutionCount++;
Assert.AreNotEqual(3, customTestClass1ExecutionCount);
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a data-driven test with [IterativeTestMethod] attribute

public class IterativeTestMethodAttribute : TestMethodAttribute
{
private readonly int stabilityThreshold;

public IterativeTestMethodAttribute(int stabilityThreshold)
{
this.stabilityThreshold = stabilityThreshold;
}

public override TestResult[] Execute(ITestMethod testMethod)
{
var results = new List<TestResult>();
for (int count = 0; count < this.stabilityThreshold; count++)
{
var testResults = base.Execute(testMethod);
foreach (var testResult in testResults)
{
testResult.DisplayName = $"{testMethod.TestMethodName} - Execution number {count + 1}";
}
results.AddRange(testResults);
}

return results.ToArray();
}
}

public class IterativeTestClassAttribute : TestClassAttribute
{
private readonly int stabilityThreshold;

public IterativeTestClassAttribute(int stabilityThreshold)
{
this.stabilityThreshold = stabilityThreshold;
}

public override TestMethodAttribute GetTestMethodAttribute(TestMethodAttribute testMethodAttribute)
{
if (testMethodAttribute is IterativeTestMethodAttribute) return testMethodAttribute;

return new IterativeTestMethodAttribute(this.stabilityThreshold);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DynamicDataExTests.cs" />
<Compile Include="DynamicDataExMoreTests.cs" />
<Compile Include="CustomTestExTests.cs" />
<Compile Include="TestDataSourceExTests.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,37 @@ public void RunTestMethodForTestThrowingExceptionShouldReturnUnitTestResultWithF
StringAssert.Contains(results[0].ErrorMessage, "Exception thrown while executing test");
}

[TestMethodV1]
public void RunTestMethodForMultipleResultsReturnMultipleResults()
{
var testMethodAttributeMock = new Mock<UTF.TestMethodAttribute>();
testMethodAttributeMock.Setup(_ => _.Execute(It.IsAny<UTF.ITestMethod>())).Returns(new[]
{
new UTF.TestResult { Outcome = UTF.UnitTestOutcome.Passed },
new UTF.TestResult { Outcome = UTF.UnitTestOutcome.Failed }
});

var localTestMethodOptions = new TestMethodOptions
{
Timeout = 200,
Executor = testMethodAttributeMock.Object,
TestContext = this.testContextImplementation,
ExpectedException = null
};

var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, localTestMethodOptions, null);
var testMethodRunner = new TestMethodRunner(testMethodInfo, this.testMethod, this.testContextImplementation, false);

var results = testMethodRunner.Execute();
Assert.AreEqual(2, results.Length);
Assert.AreEqual(AdapterTestOutcome.Passed, results[0].Outcome);
Assert.AreEqual(AdapterTestOutcome.Failed, results[1].Outcome);
}

[TestMethodV1]
public void RunTestMethodForPassingTestThrowingExceptionShouldReturnUnitTestResultWithPassedOutcome()
{
var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, this.testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed });
var testMethodInfo = new TestableTestmethodInfo(this.methodInfo, this.testClassInfo, this.testMethodOptions, () => new UTF.TestResult() { Outcome = UTF.UnitTestOutcome.Passed });
var testMethodRunner = new TestMethodRunner(testMethodInfo, this.testMethod, this.testContextImplementation, false);

var results = testMethodRunner.Execute();
Expand Down