From ca09a3d19043315c38e0178d181f84435a536c05 Mon Sep 17 00:00:00 2001 From: Andre Niggemann Date: Thu, 11 Jul 2024 11:14:56 +0200 Subject: [PATCH] InitialDesign for MAssert and modified Transition Test as an example for the usage. Feel free to break the tests and switch between Assert.That and MAssert.That to see the effects. --- Directory.build.props | 2 +- MORYX-Framework.sln | 7 ++ src/Moryx.TestTools.NUnit/MAssert.cs | 54 +++++++++++++++ .../Moryx.TestTools.NUnit.csproj | 13 ++++ src/Tests/Moryx.Tests/Moryx.Tests.csproj | 1 + .../Moryx.Tests/Workplans/TransitionTests.cs | 65 +++++++++++-------- 6 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 src/Moryx.TestTools.NUnit/MAssert.cs create mode 100644 src/Moryx.TestTools.NUnit/Moryx.TestTools.NUnit.csproj diff --git a/Directory.build.props b/Directory.build.props index 52d408eb4..c735b57d6 100644 --- a/Directory.build.props +++ b/Directory.build.props @@ -1,6 +1,6 @@ - 9.0 + 10.0 diff --git a/MORYX-Framework.sln b/MORYX-Framework.sln index 4b91aaf84..a62cf04a1 100644 --- a/MORYX-Framework.sln +++ b/MORYX-Framework.sln @@ -118,6 +118,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Runtime.Endpoints.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moryx.Runtime.Endpoints.IntegrationTests", "src\Tests\Moryx.Runtime.Endpoints.IntegrationTests\Moryx.Runtime.Endpoints.IntegrationTests.csproj", "{4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.TestTools.NUnit", "src\Moryx.TestTools.NUnit\Moryx.TestTools.NUnit.csproj", "{6FF878E0-AF61-4C3A-9B9C-71C35A949E51}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Moryx.TestTools.IntegrationTest", "src\Moryx.TestTools.IntegrationTest\Moryx.TestTools.IntegrationTest.csproj", "{C949164C-0345-4893-9E4C-A79BC1F93F85}" EndProject Global @@ -298,6 +300,10 @@ Global {4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8}.Release|Any CPU.Build.0 = Release|Any CPU + {6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FF878E0-AF61-4C3A-9B9C-71C35A949E51}.Release|Any CPU.Build.0 = Release|Any CPU {C949164C-0345-4893-9E4C-A79BC1F93F85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C949164C-0345-4893-9E4C-A79BC1F93F85}.Debug|Any CPU.Build.0 = Debug|Any CPU {C949164C-0345-4893-9E4C-A79BC1F93F85}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -346,6 +352,7 @@ Global {FEB3BA44-2CD9-445A-ABF2-C92378C443F7} = {0A466330-6ED6-4861-9C94-31B1949CDDB9} {7792C4E0-6D07-42C9-AC29-BAB76836FC11} = {0A466330-6ED6-4861-9C94-31B1949CDDB9} {4FFB98A7-9A4C-476F-8BCC-C19B7F757BF8} = {8517D209-5BC1-47BD-A7C7-9CF9ADD9F5B6} + {6FF878E0-AF61-4C3A-9B9C-71C35A949E51} = {953AAE25-26C8-4A28-AB08-61BAFE41B22F} {C949164C-0345-4893-9E4C-A79BC1F93F85} = {953AAE25-26C8-4A28-AB08-61BAFE41B22F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/Moryx.TestTools.NUnit/MAssert.cs b/src/Moryx.TestTools.NUnit/MAssert.cs new file mode 100644 index 000000000..8c75378d0 --- /dev/null +++ b/src/Moryx.TestTools.NUnit/MAssert.cs @@ -0,0 +1,54 @@ +using NUnit.Framework; +using NUnit.Framework.Constraints; +using NUnit.Framework.Internal; +using System; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using System.Xml.Linq; + +namespace Moryx.TestTools.NUnit +{ + public abstract class MAssert + { + public static void That(bool condition, string? message = null, [CallerArgumentExpression(nameof(condition))] string? predicateExpression = null) + { + That(condition, Is.True, message, predicateExpression); + } + public static void That(Func predicate, string? message = null, [CallerArgumentExpression(nameof(predicate))]string? predicateExpression = null) + { + That(predicate, Is.True, message, predicateExpression); + } + + public static void That(T actual, IResolveConstraint constraint, string? message = null, [CallerArgumentExpression(nameof(actual))] string? predicateExpression = null) + { + if (message != null) + { + message = $"{message}\nExpression: {predicateExpression}"; + } + else + { + message = predicateExpression; + } + Assert.That(actual, constraint, message); + } + + public static void That(Func actualExpression, Constraint constraint, string? message = null, [CallerArgumentExpression(nameof(actualExpression))] string? predicateExpression = null) + { + if (message != null) + { + message = $"{message}\nExpression: {predicateExpression}"; + } + else + { + message = predicateExpression; + } + int fails = TestExecutionContext.CurrentContext.CurrentResult.PendingFailures; + T value = default(T)!; + Assert.That(() => value = actualExpression(), new ThrowsNothingConstraint(), $"{message}\nExpected {constraint.Description} and"); + if (TestExecutionContext.CurrentContext.CurrentResult.PendingFailures > fails) return; // TODO: Check if we there could be multithreading issues and whether or not we care + Assert.That(value, constraint, message); + } + } + +} diff --git a/src/Moryx.TestTools.NUnit/Moryx.TestTools.NUnit.csproj b/src/Moryx.TestTools.NUnit/Moryx.TestTools.NUnit.csproj new file mode 100644 index 000000000..06fb36032 --- /dev/null +++ b/src/Moryx.TestTools.NUnit/Moryx.TestTools.NUnit.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/Tests/Moryx.Tests/Moryx.Tests.csproj b/src/Tests/Moryx.Tests/Moryx.Tests.csproj index 1ff29d6a4..ce8ad8b6e 100644 --- a/src/Tests/Moryx.Tests/Moryx.Tests.csproj +++ b/src/Tests/Moryx.Tests/Moryx.Tests.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Tests/Moryx.Tests/Workplans/TransitionTests.cs b/src/Tests/Moryx.Tests/Workplans/TransitionTests.cs index a001cf612..c5852a3ac 100644 --- a/src/Tests/Moryx.Tests/Workplans/TransitionTests.cs +++ b/src/Tests/Moryx.Tests/Workplans/TransitionTests.cs @@ -1,8 +1,10 @@ // Copyright (c) 2023, Phoenix Contact GmbH & Co. KG // Licensed under the Apache License, Version 2.0 +using System; using System.Collections.Generic; using System.Linq; +using Moryx.TestTools.NUnit; using Moryx.Workplans; using Moryx.Workplans.Transitions; using NUnit.Framework; @@ -56,14 +58,15 @@ public void SplitTransition() // Act trans.Initialize(); _inputs[0].Add(_token); - + // Assert - Assert.AreEqual(0, _inputs[0].Tokens.Count()); - Assert.IsTrue(_outputs.All(o => o.Tokens.Count() == 1)); - Assert.IsInstanceOf(_outputs[0].Tokens.First()); - Assert.IsInstanceOf(_outputs[1].Tokens.First()); - Assert.AreEqual(_token, ((SplitToken)_outputs[0].Tokens.First()).Original); - Assert.AreEqual(_token, ((SplitToken)_outputs[1].Tokens.First()).Original); + Assert.Multiple(() => + { + MAssert.That(_inputs[0].Tokens, Is.Empty); + MAssert.That(_outputs.Select(o => o.Tokens), Has.All.Count.EqualTo(1)); + MAssert.That(() => ((SplitToken)_outputs[0].Tokens.First()).Original, Is.EqualTo(_token)); + MAssert.That(() => ((SplitToken)_outputs[1].Tokens.First()).Original, Is.EqualTo(_token)); + }); } [Test] @@ -83,11 +86,13 @@ public void JoinTransition() trans.Initialize(); _inputs[0].Add(split1); _inputs[1].Add(split2); - // Assert - Assert.IsTrue(_inputs.All(i => !i.Tokens.Any())); - Assert.AreEqual(1, _outputs[0].Tokens.Count()); - Assert.AreEqual(_token, _outputs[0].Tokens.First()); + Assert.Multiple(() => + { + MAssert.That(_inputs.All(i => !i.Tokens.Any())); + MAssert.That(_outputs[0].Tokens.Count(), Is.EqualTo(1), "The split token should be joined into one"); + MAssert.That(_outputs[0].Tokens.First(), Is.EqualTo(_token)); + }); } [TestCase(0, Description = "Place only one split token on the first input")] @@ -108,9 +113,12 @@ public void IncompleteJoinTransition(int index) _inputs[index].Add(split); // Assert - Assert.AreEqual(1, _inputs[index].Tokens.Count()); - Assert.AreEqual(0, _inputs[(index + 1) % 2].Tokens.Count()); - Assert.AreEqual(0, _outputs[0].Tokens.Count()); + Assert.Multiple(() => + { + MAssert.That(_inputs[index].Tokens, Has.Count.EqualTo(1)); + MAssert.That(_inputs[(index + 1) % 2].Tokens, Is.Empty); + MAssert.That(_outputs[0].Tokens, Is.Empty); + }); } [Test] @@ -138,10 +146,12 @@ public void SubWorkplanTransition() _inputs[0].Add(_token); // Assert - Assert.AreEqual(0, _inputs[0].Tokens.Count()); - Assert.AreEqual(_token, _outputs[0].Tokens.First()); - Assert.AreEqual(2, triggered.Count); - Assert.IsTrue(triggered.All(t => t is DummyTransition)); + Assert.Multiple(() => { + MAssert.That(_inputs[0].Tokens, Is.Empty); + MAssert.That(() => _outputs[0].Tokens.First(), Is.EqualTo(_token)); + MAssert.That(triggered.Count, Is.EqualTo(2)); + MAssert.That(triggered, Has.All.InstanceOf()); + }); } [Test] @@ -171,14 +181,17 @@ public void SubworkplanPause() trans.Resume(); // Assert - Assert.AreEqual(0, _inputs[0].Tokens.Count()); - Assert.AreEqual(_token, _outputs[0].Tokens.First()); - Assert.AreEqual(1, triggered.Count); - Assert.IsInstanceOf(state); - var snapshot = (WorkplanSnapshot)state; - Assert.AreEqual(1, snapshot.Holders.Length); - var stepId = workplan.Steps.First(s => s is PausableStep).Id; - Assert.AreEqual(stepId, snapshot.Holders[0].HolderId); + Assert.Multiple(() => + { + MAssert.That(_inputs[0].Tokens, Is.Empty); + MAssert.That(_outputs[0].Tokens.First(), Is.EqualTo(_token)); + MAssert.That(triggered, Has.Count.EqualTo(1)); + MAssert.That(state, Is.InstanceOf()); + var snapshot = (WorkplanSnapshot)state; + MAssert.That(snapshot.Holders, Has.Length.EqualTo(1)); + var stepId = workplan.Steps.First(s => s is PausableStep).Id; + MAssert.That(snapshot.Holders[0].HolderId, Is.EqualTo(stepId)); + }); } } }