diff --git a/CHANGELOG.md b/CHANGELOG.md index a7bc667..04b836e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Represents the **NuGet** versions. +## v5.6.3 +- *Fixed:* `TestSetUp` fixed to load _all_ assemblies on start up (versus selective) to ensure all `OneOffTestSetUpAttribute` implementations are executed prior to any test executions. +- *Fixed:* Added `IJsonSerializer.Clone()` and `JsonElementComparerOptions.Clone` to ensure cross test contamination does not occur. + ## v5.6.2 - *Fixed:* Republish packages with a new version; last publish was incomplete. diff --git a/Common.targets b/Common.targets index 02eeb7f..5bd27e5 100644 --- a/Common.targets +++ b/Common.targets @@ -1,6 +1,6 @@  - 5.6.2 + 5.6.3 preview Avanade Avanade diff --git a/src/UnitTestEx/Abstractions/OneOffTestSetUpAttribute.cs b/src/UnitTestEx/Abstractions/OneOffTestSetUpAttribute.cs index 33c962f..da8bf68 100644 --- a/src/UnitTestEx/Abstractions/OneOffTestSetUpAttribute.cs +++ b/src/UnitTestEx/Abstractions/OneOffTestSetUpAttribute.cs @@ -14,9 +14,6 @@ namespace UnitTestEx.Abstractions [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] public class OneOffTestSetUpAttribute(Type type) : Attribute { - private static readonly object _oneOffSetUpLock = new(); - private static readonly HashSet _oneOffSetUpTypes = []; - /// /// Performs the set up for the specified where configured using the . /// diff --git a/src/UnitTestEx/Abstractions/TesterBase.cs b/src/UnitTestEx/Abstractions/TesterBase.cs index 50a3f50..e9144c9 100644 --- a/src/UnitTestEx/Abstractions/TesterBase.cs +++ b/src/UnitTestEx/Abstractions/TesterBase.cs @@ -35,6 +35,8 @@ public abstract class TesterBase /// static TesterBase() { + TestSetUp.Force(); + try { var fi = new FileInfo(Path.Combine(Environment.CurrentDirectory, "appsettings.unittest.json")); diff --git a/src/UnitTestEx/Json/IJsonSerializer.cs b/src/UnitTestEx/Json/IJsonSerializer.cs index 88efdd6..16aeca1 100644 --- a/src/UnitTestEx/Json/IJsonSerializer.cs +++ b/src/UnitTestEx/Json/IJsonSerializer.cs @@ -45,5 +45,11 @@ public interface IJsonSerializer /// The JSON . /// The corresponding typed value. T? Deserialize(string json); + + /// + /// Clones the . + /// + /// The cloned . + IJsonSerializer Clone(); } } \ No newline at end of file diff --git a/src/UnitTestEx/Json/JsonElementComparerOptions.cs b/src/UnitTestEx/Json/JsonElementComparerOptions.cs index 3fa9927..a03053a 100644 --- a/src/UnitTestEx/Json/JsonElementComparerOptions.cs +++ b/src/UnitTestEx/Json/JsonElementComparerOptions.cs @@ -77,7 +77,7 @@ public static JsonElementComparerOptions Default MaxDifferences = MaxDifferences, ValueComparison = ValueComparison, NullComparison = NullComparison, - JsonSerializer = JsonSerializer + JsonSerializer = JsonSerializer?.Clone() }; } } \ No newline at end of file diff --git a/src/UnitTestEx/Json/JsonSerializer.cs b/src/UnitTestEx/Json/JsonSerializer.cs index 1f86717..d0193ca 100644 --- a/src/UnitTestEx/Json/JsonSerializer.cs +++ b/src/UnitTestEx/Json/JsonSerializer.cs @@ -64,5 +64,8 @@ public JsonSerializer(Stj.JsonSerializerOptions? options = null) /// public string Serialize(T value, JsonWriteFormat? format = null) => Stj.JsonSerializer.Serialize(value, format == null || format.Value == JsonWriteFormat.None ? Options : IndentedOptions); + + /// + public IJsonSerializer Clone() => new JsonSerializer(new Stj.JsonSerializerOptions(Options)); } } \ No newline at end of file diff --git a/src/UnitTestEx/TestSetUp.cs b/src/UnitTestEx/TestSetUp.cs index 3046a27..552a808 100644 --- a/src/UnitTestEx/TestSetUp.cs +++ b/src/UnitTestEx/TestSetUp.cs @@ -25,6 +25,7 @@ public class TestSetUp : ICloneable { private static readonly SemaphoreSlim _semaphore = new(1, 1); private static readonly ConcurrentQueue _autoSetUpOutputs = new(); + private static readonly ConcurrentDictionary _registeredAssemblies = new(); private TestSetUp? _clonedFrom; private bool _setUpSet = false; @@ -40,13 +41,16 @@ public class TestSetUp : ICloneable /// Wires up the invocation whenever an is loaded. static TestSetUp() { - // Load dependent UnitTestEx assemblies as they may not have been loaded yet! - foreach (var fi in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).EnumerateFiles("*.dll").Where(f => f.Name.StartsWith("UnitTestEx."))) + // Load all dependent assemblies to ensure all one off test set up(s) execute before any test exection is perfomed. + foreach (var fi in new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).EnumerateFiles("*.dll")) Assembly.LoadFrom(fi.FullName); // Wire up for any assemblies already loaded. foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - OneOffTestSetUpAttribute.SetUp(assembly); + { + if (_registeredAssemblies.TryAdd(assembly.FullName!, null)) + OneOffTestSetUpAttribute.SetUp(assembly); + } // Wire up for any future assembly loading. AppDomain.CurrentDomain.AssemblyLoad += (_, e) => OneOffTestSetUpAttribute.SetUp(e.LoadedAssembly); @@ -199,8 +203,8 @@ public static void LogAutoSetUpOutputs(TestFrameworkImplementor implementor) /// The and will reference the originating unless explicitly registered (overridden) for the cloned instance. public TestSetUp Clone() => new() { - JsonSerializer = JsonSerializer, - JsonComparerOptions = JsonComparerOptions, + JsonSerializer = JsonSerializer.Clone(), + JsonComparerOptions = JsonComparerOptions.Clone(), Properties = new Dictionary(Properties), DefaultUserName = DefaultUserName, UserNameConverter = UserNameConverter, @@ -255,7 +259,7 @@ public void RegisterAutoSetUp(Func SetUpAsync(object? data = null, CancellationToken cancellationToken = default) { if (SetUpFunc == null && AutoSetUpFunc == null) - throw new InvalidOperationException("Set up can not be invoked as no set up function has been registered; please use RegisterSetUp() ot AutoRegisterSetUp() to enable."); + throw new InvalidOperationException("Set up can not be invoked as no set up function has been registered; please use RegisterSetUp() or AutoRegisterSetUp() to enable."); await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try diff --git a/tests/UnitTestEx.MSTest.Test/NewtonsoftJsonSerializer.cs b/tests/UnitTestEx.MSTest.Test/NewtonsoftJsonSerializer.cs index f24379d..065685a 100644 --- a/tests/UnitTestEx.MSTest.Test/NewtonsoftJsonSerializer.cs +++ b/tests/UnitTestEx.MSTest.Test/NewtonsoftJsonSerializer.cs @@ -66,7 +66,7 @@ public class NewtonsoftJsonSerializer : IJsonSerializer /// /// Copies the settings. /// - private Nsj.JsonSerializerSettings CopySettings(JsonWriteFormat format) + private Nsj.JsonSerializerSettings CopySettings(JsonWriteFormat? format) { var s = new Nsj.JsonSerializerSettings { @@ -89,7 +89,7 @@ private Nsj.JsonSerializerSettings CopySettings(JsonWriteFormat format) Context = Settings.Context, DateFormatString = Settings.DateFormatString, MaxDepth = Settings.MaxDepth, - Formatting = format == JsonWriteFormat.None ? Nsj.Formatting.None : Nsj.Formatting.Indented, + Formatting = format is null ? Settings.Formatting : format == JsonWriteFormat.None ? Nsj.Formatting.None : Nsj.Formatting.Indented, DateFormatHandling = Settings.DateFormatHandling, DateTimeZoneHandling = Settings.DateTimeZoneHandling, DateParseHandling = Settings.DateParseHandling, @@ -105,6 +105,9 @@ private Nsj.JsonSerializerSettings CopySettings(JsonWriteFormat format) return s; } + + /// + public IJsonSerializer Clone() => new NewtonsoftJsonSerializer(CopySettings(null)); } }