diff --git a/.reviewmark.yaml b/.reviewmark.yaml index b4e3c43..24e0e31 100644 --- a/.reviewmark.yaml +++ b/.reviewmark.yaml @@ -44,7 +44,7 @@ reviews: title: Review of All TestResults Requirements paths: - "requirements.yaml" - - "docs/reqstream/**/*.yaml" + - "docs/reqstream/test-results/**/*.yaml" - id: TestResults-IO title: Review of TestResults IO Subsystem diff --git a/docs/design/test-results/io/io.md b/docs/design/test-results/io/io.md index 6df5801..6ab28e0 100644 --- a/docs/design/test-results/io/io.md +++ b/docs/design/test-results/io/io.md @@ -36,4 +36,4 @@ The IO subsystem depends on: ## Related Requirements Requirements for the IO subsystem are in -[docs/reqstream/test-results/io/](../../reqstream/test-results/io/). +[docs/reqstream/test-results/io/](../../../reqstream/test-results/io/). diff --git a/src/DemaConsulting.TestResults/TestOutcome.cs b/src/DemaConsulting.TestResults/TestOutcome.cs index e041fc9..19c7cba 100644 --- a/src/DemaConsulting.TestResults/TestOutcome.cs +++ b/src/DemaConsulting.TestResults/TestOutcome.cs @@ -108,6 +108,7 @@ public static class TestOutcomeExtensions /// True if the outcome indicates a pass public static bool IsPassed(this TestOutcome outcome) { + // Treat outcomes where the test logic completed without failure as passed return outcome switch { TestOutcome.Passed => true, @@ -124,6 +125,7 @@ public static bool IsPassed(this TestOutcome outcome) /// True if the outcome indicates a fail public static bool IsFailed(this TestOutcome outcome) { + // Treat outcomes representing an abnormal termination or assertion failure as failed return outcome switch { TestOutcome.Failed => true, @@ -141,6 +143,7 @@ public static bool IsFailed(this TestOutcome outcome) /// True if the outcome indicates the test was executed public static bool IsExecuted(this TestOutcome outcome) { + // Treat outcomes where the test was never attempted as not executed return outcome switch { TestOutcome.NotRunnable => false, diff --git a/src/DemaConsulting.TestResults/TestResult.cs b/src/DemaConsulting.TestResults/TestResult.cs index a2de702..5db3a50 100644 --- a/src/DemaConsulting.TestResults/TestResult.cs +++ b/src/DemaConsulting.TestResults/TestResult.cs @@ -25,68 +25,92 @@ namespace DemaConsulting.TestResults; /// public sealed class TestResult { + // Identity properties — each result needs unique IDs for cross-referencing + // between the test definition and its execution record + /// - /// Gets or sets the ID of the test case + /// Gets or sets the ID of the test case. + /// Defaults to a newly generated so every test definition is uniquely identifiable. /// public Guid TestId { get; set; } = Guid.NewGuid(); /// - /// Gets or sets the ID of the test execution + /// Gets or sets the ID of the test execution. + /// Defaults to a newly generated so every execution is uniquely identifiable. /// public Guid ExecutionId { get; set; } = Guid.NewGuid(); + // Descriptive metadata — human-readable strings that identify the test in reports + /// - /// Gets or sets the name of the test case + /// Gets or sets the name of the test case. + /// Defaults to so the property is always non-null. /// public string Name { get; set; } = string.Empty; /// - /// Gets or sets the test code assembly + /// Gets or sets the test code assembly. + /// Defaults to so the property is always non-null. /// public string CodeBase { get; set; } = string.Empty; /// - /// Gets or sets the name of the class containing the test case + /// Gets or sets the name of the class containing the test case. + /// Defaults to so the property is always non-null. /// public string ClassName { get; set; } = string.Empty; /// - /// Gets or sets the name of the computer that executed the test case + /// Gets or sets the name of the computer that executed the test case. + /// Defaults to so locally-run results are attributed correctly. /// public string ComputerName { get; set; } = Environment.MachineName; + // Timing properties — record when and how long the test ran + /// - /// Gets or sets the start time of the test execution + /// Gets or sets the start time of the test execution. + /// Defaults to at construction time so ad-hoc results have a meaningful timestamp. /// public DateTime StartTime { get; set; } = DateTime.UtcNow; /// - /// Gets or sets the duration of the test execution + /// Gets or sets the duration of the test execution. + /// Defaults to so the property is always valid even when timing is unavailable. /// public TimeSpan Duration { get; set; } = TimeSpan.Zero; + // Output capture — text written to stdout/stderr during the test run + /// - /// Gets or sets the stdout output when executing the test case + /// Gets or sets the stdout output when executing the test case. + /// Defaults to so the property is always non-null. /// public string SystemOutput { get; set; } = string.Empty; /// - /// Gets or sets the stderr output when executing the test case + /// Gets or sets the stderr output when executing the test case. + /// Defaults to so the property is always non-null. /// public string SystemError { get; set; } = string.Empty; + // Result properties — the outcome and any failure details + /// - /// Gets or sets the outcome of the test case + /// Gets or sets the outcome of the test case. + /// Defaults to so a result that was never run is not mistaken for a pass. /// public TestOutcome Outcome { get; set; } = TestOutcome.NotExecuted; /// - /// Gets or sets the test case error message + /// Gets or sets the test case error message. + /// Defaults to so the property is always non-null. /// public string ErrorMessage { get; set; } = string.Empty; /// - /// Gets or sets the test case error stack trace + /// Gets or sets the test case error stack trace. + /// Defaults to so the property is always non-null. /// public string ErrorStackTrace { get; set; } = string.Empty; } diff --git a/src/DemaConsulting.TestResults/TestResults.cs b/src/DemaConsulting.TestResults/TestResults.cs index 5323c0b..80cd362 100644 --- a/src/DemaConsulting.TestResults/TestResults.cs +++ b/src/DemaConsulting.TestResults/TestResults.cs @@ -25,23 +25,31 @@ namespace DemaConsulting.TestResults; /// public sealed class TestResults { + // Identity and metadata — unique identifier and human-readable name for the run + /// - /// Gets or sets the ID of the test results + /// Gets or sets the ID of the test results. + /// Defaults to a newly generated so every test run is uniquely identifiable. /// public Guid Id { get; set; } = Guid.NewGuid(); /// - /// Gets or sets the name of the tests + /// Gets or sets the name of the tests. + /// Defaults to so the property is always non-null. /// public string Name { get; set; } = string.Empty; /// - /// Gets or sets the name of the user account running the tests + /// Gets or sets the name of the user account running the tests. + /// Defaults to so the property is always non-null. /// public string UserName { get; set; } = string.Empty; + // Results collection — the ordered list of individual test outcomes for this run + /// - /// Gets or sets the list containing each TestResult + /// Gets or sets the list containing each . + /// Defaults to an empty list so callers can add results without null-checking first. /// public List Results { get; set; } = []; } diff --git a/test/DemaConsulting.TestResults.Tests/IO/JUnitSerializerTests.cs b/test/DemaConsulting.TestResults.Tests/IO/JUnitSerializerTests.cs index f3bfc17..651ee83 100644 --- a/test/DemaConsulting.TestResults.Tests/IO/JUnitSerializerTests.cs +++ b/test/DemaConsulting.TestResults.Tests/IO/JUnitSerializerTests.cs @@ -151,7 +151,7 @@ public void JUnitSerializer_Serialize_FailedTest_IncludesFailureElement() [TestMethod] public void JUnitSerializer_Serialize_ErrorTest_IncludesErrorElement() { - // Construct test results with an error test + // Arrange - Construct test results with an error test var results = new TestResults { Name = "ErrorTests", @@ -169,11 +169,11 @@ public void JUnitSerializer_Serialize_ErrorTest_IncludesErrorElement() ] }; - // Serialize the test results + // Act - Serialize the test results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the XML structure + // Assert - Parse and verify the XML structure var doc = XDocument.Parse(xml); var testSuite = doc.Root?.Element("testsuite"); Assert.IsNotNull(testSuite); @@ -181,7 +181,7 @@ public void JUnitSerializer_Serialize_ErrorTest_IncludesErrorElement() Assert.AreEqual("0", testSuite.Attribute("failures")?.Value); Assert.AreEqual("1", testSuite.Attribute("errors")?.Value); - // Verify test case with error + // Assert - Verify test case with error var testCase = testSuite.Element("testcase"); Assert.IsNotNull(testCase); var error = testCase.Element("error"); @@ -196,7 +196,7 @@ public void JUnitSerializer_Serialize_ErrorTest_IncludesErrorElement() [TestMethod] public void JUnitSerializer_Serialize_SkippedTest_IncludesSkippedElement() { - // Construct test results with a skipped test + // Arrange - Construct test results with a skipped test var results = new TestResults { Name = "SkippedTests", @@ -213,11 +213,11 @@ public void JUnitSerializer_Serialize_SkippedTest_IncludesSkippedElement() ] }; - // Serialize the test results + // Act - Serialize the test results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the XML structure + // Assert - Parse and verify the XML structure var doc = XDocument.Parse(xml); var testSuite = doc.Root?.Element("testsuite"); Assert.IsNotNull(testSuite); @@ -226,7 +226,7 @@ public void JUnitSerializer_Serialize_SkippedTest_IncludesSkippedElement() Assert.AreEqual("0", testSuite.Attribute("errors")?.Value); Assert.AreEqual("1", testSuite.Attribute("skipped")?.Value); - // Verify test case with skipped element + // Assert - Verify test case with skipped element var testCase = testSuite.Element("testcase"); Assert.IsNotNull(testCase); var skipped = testCase.Element("skipped"); @@ -240,7 +240,7 @@ public void JUnitSerializer_Serialize_SkippedTest_IncludesSkippedElement() [TestMethod] public void JUnitSerializer_Serialize_TestWithOutput_IncludesSystemOutAndErr() { - // Construct test results with system output + // Arrange - Construct test results with system output var results = new TestResults { Name = "OutputTests", @@ -258,21 +258,21 @@ public void JUnitSerializer_Serialize_TestWithOutput_IncludesSystemOutAndErr() ] }; - // Serialize the test results + // Act - Serialize the test results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the XML structure + // Assert - Parse and verify the XML structure var doc = XDocument.Parse(xml); var testCase = doc.Root?.Element("testsuite")?.Element("testcase"); Assert.IsNotNull(testCase); - // Verify system-out element + // Assert - Verify system-out element var systemOut = testCase.Element("system-out"); Assert.IsNotNull(systemOut); Assert.AreEqual("Standard output message", systemOut.Value); - // Verify system-err element + // Assert - Verify system-err element var systemErr = testCase.Element("system-err"); Assert.IsNotNull(systemErr); Assert.AreEqual("Standard error message", systemErr.Value); @@ -284,7 +284,7 @@ public void JUnitSerializer_Serialize_TestWithOutput_IncludesSystemOutAndErr() [TestMethod] public void JUnitSerializer_Serialize_MultipleTestsInClasses_GroupsByClassName() { - // Construct test results with multiple tests + // Arrange - Construct test results with multiple tests var results = new TestResults { Name = "MultipleTests", @@ -315,27 +315,27 @@ public void JUnitSerializer_Serialize_MultipleTestsInClasses_GroupsByClassName() ] }; - // Serialize the test results + // Act - Serialize the test results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the XML structure + // Assert - Parse and verify the XML structure var doc = XDocument.Parse(xml); var root = doc.Root; Assert.IsNotNull(root); - // Verify two test suites (one for each class) + // Assert - Verify two test suites (one for each class) var testSuites = root.Elements("testsuite").ToList(); Assert.HasCount(2, testSuites); - // Verify first test suite (Class1) + // Assert - Verify first test suite (Class1) var suite1 = testSuites[0]; Assert.AreEqual("Class1", suite1.Attribute("name")?.Value); Assert.AreEqual("2", suite1.Attribute("tests")?.Value); Assert.AreEqual("1", suite1.Attribute("failures")?.Value); Assert.AreEqual("1.500", suite1.Attribute("time")?.Value); - // Verify second test suite (Class2) + // Assert - Verify second test suite (Class2) var suite2 = testSuites[1]; Assert.AreEqual("Class2", suite2.Attribute("name")?.Value); Assert.AreEqual("1", suite2.Attribute("tests")?.Value); @@ -349,7 +349,7 @@ public void JUnitSerializer_Serialize_MultipleTestsInClasses_GroupsByClassName() [TestMethod] public void JUnitSerializer_Serialize_EmptyClassName_UsesDefaultSuite() { - // Construct test results with empty class name + // Arrange - Construct test results with empty class name var results = new TestResults { Name = "EmptyClassTests", @@ -365,11 +365,11 @@ public void JUnitSerializer_Serialize_EmptyClassName_UsesDefaultSuite() ] }; - // Serialize the test results + // Act - Serialize the test results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the XML structure + // Assert - Parse and verify the XML structure var doc = XDocument.Parse(xml); var testSuite = doc.Root?.Element("testsuite"); Assert.IsNotNull(testSuite); @@ -386,10 +386,10 @@ public void JUnitSerializer_Serialize_EmptyClassName_UsesDefaultSuite() [TestMethod] public void JUnitSerializer_Serialize_UsageExample_ProducesValidJUnitXml() { - // Create a TestResults instance matching the usage example + // Arrange - Create a TestResults instance matching the usage example var results = new TestResults { Name = "SomeTests" }; - // Add some results + // Arrange - Add some results results.Results.Add( new TestResult { @@ -412,34 +412,34 @@ public void JUnitSerializer_Serialize_UsageExample_ProducesValidJUnitXml() ErrorStackTrace = "at SomeTestClass.Test2() in Test.cs:line 15" }); - // Serialize the results + // Act - Serialize the results var xml = JUnitSerializer.Serialize(results); Assert.IsNotNull(xml); - // Parse and verify the structure matches expected JUnit format + // Assert - Parse and verify the structure matches expected JUnit format var doc = XDocument.Parse(xml); var root = doc.Root; Assert.IsNotNull(root); Assert.AreEqual("testsuites", root.Name.LocalName); Assert.AreEqual("SomeTests", root.Attribute("name")?.Value); - // Verify test suite + // Assert - Verify test suite var testSuite = root.Element("testsuite"); Assert.IsNotNull(testSuite); Assert.AreEqual("SomeTestClass", testSuite.Attribute("name")?.Value); Assert.AreEqual("2", testSuite.Attribute("tests")?.Value); Assert.AreEqual("1", testSuite.Attribute("failures")?.Value); - // Verify both test cases are present + // Assert - Verify both test cases are present var testCases = testSuite.Elements("testcase").ToList(); Assert.HasCount(2, testCases); - // Verify passed test + // Assert - Verify passed test var passedTest = testCases.FirstOrDefault(tc => tc.Attribute("name")?.Value == "Test1"); Assert.IsNotNull(passedTest); Assert.IsNull(passedTest.Element("failure")); - // Verify failed test + // Assert - Verify failed test var failedTest = testCases.FirstOrDefault(tc => tc.Attribute("name")?.Value == "Test2"); Assert.IsNotNull(failedTest); var failure = failedTest.Element("failure"); @@ -490,7 +490,7 @@ public void JUnitSerializer_Deserialize_BasicJUnitXml_ReturnsTestResults() [TestMethod] public void JUnitSerializer_Deserialize_FailedTest_ReturnsFailureDetails() { - // Deserialize the test results object with a failed test + // Arrange and Act - Deserialize the test results object with a failed test var results = JUnitSerializer.Deserialize( """ @@ -504,11 +504,11 @@ public void JUnitSerializer_Deserialize_FailedTest_ReturnsFailureDetails() """); Assert.IsNotNull(results); - // Assert results information + // Assert - Verify results information Assert.AreEqual("FailureTests", results.Name); Assert.HasCount(1, results.Results); - // Assert test result information + // Assert - Verify test result information var result = results.Results[0]; Assert.AreEqual("Test2", result.Name); Assert.AreEqual("MyTestClass", result.ClassName); @@ -523,7 +523,7 @@ public void JUnitSerializer_Deserialize_FailedTest_ReturnsFailureDetails() [TestMethod] public void JUnitSerializer_Deserialize_ErrorTest_ReturnsErrorDetails() { - // Deserialize the test results object with an error test + // Arrange and Act - Deserialize the test results object with an error test var results = JUnitSerializer.Deserialize( """ @@ -537,7 +537,7 @@ public void JUnitSerializer_Deserialize_ErrorTest_ReturnsErrorDetails() """); Assert.IsNotNull(results); - // Assert test result information + // Assert - Verify test result information var result = results.Results[0]; Assert.AreEqual("Test3", result.Name); Assert.AreEqual(TestOutcome.Error, result.Outcome); @@ -551,7 +551,7 @@ public void JUnitSerializer_Deserialize_ErrorTest_ReturnsErrorDetails() [TestMethod] public void JUnitSerializer_Deserialize_SkippedTest_ReturnsSkippedStatus() { - // Deserialize the test results object with a skipped test + // Arrange and Act - Deserialize the test results object with a skipped test var results = JUnitSerializer.Deserialize( """ @@ -565,7 +565,7 @@ public void JUnitSerializer_Deserialize_SkippedTest_ReturnsSkippedStatus() """); Assert.IsNotNull(results); - // Assert test result information + // Assert - Verify test result information var result = results.Results[0]; Assert.AreEqual("Test4", result.Name); Assert.AreEqual(TestOutcome.NotExecuted, result.Outcome); @@ -578,7 +578,7 @@ public void JUnitSerializer_Deserialize_SkippedTest_ReturnsSkippedStatus() [TestMethod] public void JUnitSerializer_Deserialize_TestWithOutput_ReturnsSystemOutput() { - // Deserialize the test results object with system output + // Arrange and Act - Deserialize the test results object with system output var results = JUnitSerializer.Deserialize( """ @@ -593,7 +593,7 @@ public void JUnitSerializer_Deserialize_TestWithOutput_ReturnsSystemOutput() """); Assert.IsNotNull(results); - // Assert test result information + // Assert - Verify test result information var result = results.Results[0]; Assert.AreEqual("Test5", result.Name); Assert.AreEqual("Standard output message", result.SystemOutput); @@ -606,7 +606,7 @@ public void JUnitSerializer_Deserialize_TestWithOutput_ReturnsSystemOutput() [TestMethod] public void JUnitSerializer_Deserialize_MultipleTestSuites_ReturnsAllTests() { - // Deserialize the test results object with multiple test suites + // Arrange and Act - Deserialize the test results object with multiple test suites var results = JUnitSerializer.Deserialize( """ @@ -624,22 +624,22 @@ public void JUnitSerializer_Deserialize_MultipleTestSuites_ReturnsAllTests() """); Assert.IsNotNull(results); - // Assert results information + // Assert - Verify results information Assert.AreEqual("MultipleTests", results.Name); Assert.HasCount(3, results.Results); - // Verify first test + // Assert - Verify first test var test1 = results.Results[0]; Assert.AreEqual("Test1", test1.Name); Assert.AreEqual("Class1", test1.ClassName); Assert.AreEqual(TestOutcome.Passed, test1.Outcome); - // Verify second test + // Assert - Verify second test var test2 = results.Results[1]; Assert.AreEqual("Test2", test2.Name); Assert.AreEqual(TestOutcome.Failed, test2.Outcome); - // Verify third test + // Assert - Verify third test var test3 = results.Results[2]; Assert.AreEqual("Test3", test3.Name); Assert.AreEqual("Class2", test3.ClassName); @@ -652,7 +652,7 @@ public void JUnitSerializer_Deserialize_MultipleTestSuites_ReturnsAllTests() [TestMethod] public void JUnitSerializer_Deserialize_DefaultSuite_ReturnsEmptyClassName() { - // Deserialize the test results object with DefaultSuite + // Arrange and Act - Deserialize the test results object with DefaultSuite var results = JUnitSerializer.Deserialize( """ @@ -664,7 +664,7 @@ public void JUnitSerializer_Deserialize_DefaultSuite_ReturnsEmptyClassName() """); Assert.IsNotNull(results); - // Assert test result information - DefaultSuite should be converted to empty string + // Assert - Verify test result information - DefaultSuite should be converted to empty string var result = results.Results[0]; Assert.AreEqual("Test1", result.Name); Assert.AreEqual(string.Empty, result.ClassName); @@ -737,7 +737,7 @@ public void JUnitSerializer_Serialize_ThenDeserialize_PreservesTestData() [TestMethod] public void JUnitSerializer_Deserialize_MissingTimeAttribute_DefaultsToZero() { - // Deserialize the test results object without time attribute + // Arrange and Act - Deserialize the test results object without time attribute var results = JUnitSerializer.Deserialize( """ diff --git a/test/DemaConsulting.TestResults.Tests/TestOutcomeTests.cs b/test/DemaConsulting.TestResults.Tests/TestOutcomeTests.cs index ea63099..76b6dcf 100644 --- a/test/DemaConsulting.TestResults.Tests/TestOutcomeTests.cs +++ b/test/DemaConsulting.TestResults.Tests/TestOutcomeTests.cs @@ -34,6 +34,9 @@ public class TestOutcomeTests [TestMethod] public void TestOutcome_IsPassed_PassedOutcome_ReturnsTrue() { + // Arrange - no setup required; testing enum extension directly + + // Act and Assert - verify each outcome returns the expected IsPassed result Assert.IsFalse(TestOutcome.Error.IsPassed()); Assert.IsFalse(TestOutcome.Failed.IsPassed()); Assert.IsFalse(TestOutcome.Timeout.IsPassed()); @@ -56,6 +59,9 @@ public void TestOutcome_IsPassed_PassedOutcome_ReturnsTrue() [TestMethod] public void TestOutcome_IsFailed_FailedOutcome_ReturnsTrue() { + // Arrange - no setup required; testing enum extension directly + + // Act and Assert - verify each outcome returns the expected IsFailed result Assert.IsTrue(TestOutcome.Error.IsFailed()); Assert.IsTrue(TestOutcome.Failed.IsFailed()); Assert.IsTrue(TestOutcome.Timeout.IsFailed()); @@ -78,6 +84,9 @@ public void TestOutcome_IsFailed_FailedOutcome_ReturnsTrue() [TestMethod] public void TestOutcome_IsExecuted_AllOutcomes_ReturnsExpectedResult() { + // Arrange - no setup required; testing enum extension directly + + // Act and Assert - verify each outcome returns the expected IsExecuted result Assert.IsTrue(TestOutcome.Error.IsExecuted()); Assert.IsTrue(TestOutcome.Failed.IsExecuted()); Assert.IsTrue(TestOutcome.Timeout.IsExecuted()); @@ -100,6 +109,9 @@ public void TestOutcome_IsExecuted_AllOutcomes_ReturnsExpectedResult() [TestMethod] public void TestOutcome_IsExecuted_NotExecutedOutcome_ReturnsFalse() { + // Arrange - no setup required; testing enum extension directly + + // Act and Assert - verify NotExecuted outcome returns false Assert.IsFalse(TestOutcome.NotExecuted.IsExecuted()); } }