diff --git a/Content/GameSettings.json b/Content/GameSettings.json new file mode 100644 index 0000000..f4ab815 --- /dev/null +++ b/Content/GameSettings.json @@ -0,0 +1,9 @@ +{ + "ID": "6888874e4229953ae4692194ef19ddf2", + "TypeName": "FlaxEditor.Content.Settings.GameSettings", + "EngineBuild": 6187, + "Data": { + "ProductName": "Simple Unit Testing", + "FirstScene": "00000000000000000000000000000000" +} +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..43bfce9 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Unit Testing Plugin for the FlaxEngine + diff --git a/Source/Assert.cs b/Source/Editor/Assert.cs similarity index 54% rename from Source/Assert.cs rename to Source/Editor/Assert.cs index 0427ce8..605982f 100644 --- a/Source/Assert.cs +++ b/Source/Editor/Assert.cs @@ -1,21 +1,33 @@ using System; -namespace FlaxEngine.UnitTesting +namespace FlaxCommunity.UnitTesting.Editor { /// /// Special type of exception that is used to terminate the test case early /// /// - public class SuccessException : Exception { } + public class SuccessException : Exception + { + public SuccessException() + { + } + + public SuccessException(string message) : base(message) + { + } + + public SuccessException(string message, Exception innerException) : base(message, innerException) + { + } + } public static class Assert { public static void Pass() => throw new SuccessException(); public static void Fail() => throw new Exception(); - // TODO: use Equals instead of == - public static void AreEqual(object a, object b) { if (a != b) throw new Exception(); } - public static void AreNotEqual(object a, object b) { if (a == b) throw new Exception(); } + public static void AreEqual(object a, object b) { if (!Equals(a, b)) throw new Exception(); } + public static void AreNotEqual(object a, object b) { if (Equals(a, b)) throw new Exception(); } public static void True(bool a) { if (!a) throw new Exception(); } public static void False(bool a) { if (a) throw new Exception(); } diff --git a/Source/ExampleClass.cs b/Source/Editor/ExampleClass.cs similarity index 76% rename from Source/ExampleClass.cs rename to Source/Editor/ExampleClass.cs index eeb37fb..a944eda 100644 --- a/Source/ExampleClass.cs +++ b/Source/Editor/ExampleClass.cs @@ -1,7 +1,7 @@ #if !FLAX_PLUGIN -using FlaxEngine.UnitTesting; +using FlaxCommunity.UnitTesting.Editor; -namespace UnitTests +namespace UnitTests.Editor { [TestFixture] internal class SimpleTests @@ -15,7 +15,7 @@ public void SuccessTest() [Test] public void ErrorTest() { - Assert.True(null != null); + Assert.True(1 != 1); } } @@ -23,9 +23,16 @@ public void ErrorTest() internal class SetupTests { private object Tested = null; + private object Database = null; + + [OneTimeSetUp] + public void Init() + { + Database = new object { }; + } [SetUp] - public void Setup() + public void BeforeEach() { Tested = "Test"; } @@ -37,8 +44,13 @@ public void SetupTest() } [TearDown] + public void AfterEach() + { + } + public void Dispose() { + Database = null; } } diff --git a/Source/TestAttributes.cs b/Source/Editor/TestAttributes.cs similarity index 59% rename from Source/TestAttributes.cs rename to Source/Editor/TestAttributes.cs index 44612bf..3f1e0a7 100644 --- a/Source/TestAttributes.cs +++ b/Source/Editor/TestAttributes.cs @@ -1,9 +1,12 @@ using System; -namespace FlaxEngine.UnitTesting +namespace FlaxCommunity.UnitTesting.Editor { + /// + /// A test case + /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class TestCase : Attribute + public sealed class TestCase : Attribute { public readonly object[] Attributes; public object ExpectedResult { get; set; } @@ -43,24 +46,52 @@ public TestCase(object T1, object T2, object T3, object T4, object T5, object T6 } } + /// + /// A single test + /// [AttributeUsage(AttributeTargets.Method)] - public class Test : Attribute + public sealed class Test : Attribute { } + /// + /// Executed before every single test + /// [AttributeUsage(AttributeTargets.Method)] - public class SetUp : Attribute + public sealed class SetUp : Attribute { } + /// + /// Executed after every single test + /// [AttributeUsage(AttributeTargets.Method)] - public class TearDown : Attribute + public sealed class TearDown : Attribute { } + /// + /// Executed before all tests + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class OneTimeSetUp : Attribute + { + } + + /// + /// Executed after all tests + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class OneTimeTearDown : Attribute + { + } + + /// + /// Specifies a class as a unit test class + /// [AttributeUsage(AttributeTargets.Class)] - public class TestFixture : Attribute + public sealed class TestFixture : Attribute { } } \ No newline at end of file diff --git a/Source/Editor/TestRunner.cs b/Source/Editor/TestRunner.cs index e920ada..85d2c83 100644 --- a/Source/Editor/TestRunner.cs +++ b/Source/Editor/TestRunner.cs @@ -1,69 +1,81 @@ using FlaxEditor; using FlaxEditor.GUI; +using FlaxEngine; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using FlaxCommunity.UnitTesting; -namespace FlaxEngine.UnitTesting.Editor +namespace FlaxCommunity.UnitTesting.Editor { public class TestRunner : EditorPlugin { - private static List suites = new List(); - private MainMenuButton mmBtn; + private static readonly List _suites = new List(); + private MainMenuButton _mmBtn; public override PluginDescription Description => new PluginDescription { Author = "Lukáš Jech", - AuthorUrl ="https://lukas.jech.me", + AuthorUrl = "https://lukas.jech.me", Category = "Unit Testing", Description = "Simple unit testing framework", IsAlpha = false, IsBeta = false, Name = "Simple Unit Testing", - SupportedPlatforms = new PlatformType[] {PlatformType.Windows}, - Version = new Version(1,0), - RepositoryUrl = "https://github.com/klukule/flax-ut" + SupportedPlatforms = new PlatformType[] { PlatformType.Windows }, + Version = new Version(1, 1), + RepositoryUrl = "https://github.com/FlaxCommunityProjects/FlaxUnitTesting" }; - + public override void InitializeEditor() { base.InitializeEditor(); - mmBtn = Editor.UI.MainMenu.AddButton("Unit Tests"); - mmBtn.ContextMenu.AddButton("Run unit tests").Clicked += RunTests; + _mmBtn = Editor.UI.MainMenu.AddButton("Unit Tests"); + _mmBtn.ContextMenu.AddButton("Run unit tests").Clicked += RunTests; + FlaxEditor.Scripting.ScriptsBuilder.ScriptsReloadBegin += ScriptsBuilder_ScriptsReloadBegin; + } + private void ScriptsBuilder_ScriptsReloadBegin() + { + // Clear type information as per warning https://docs.flaxengine.com/manual/scripting/plugins/index.html + _suites.Clear(); } public override void Deinitialize() { base.Deinitialize(); - if (mmBtn != null) + if (_mmBtn != null) { - mmBtn.Dispose(); - mmBtn = null; + _mmBtn.Dispose(); + _mmBtn = null; } } private static void GatherTests() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); - suites.Clear(); + _suites.Clear(); foreach (var assembly in assemblies) foreach (var type in assembly.GetTypes()) if (type.GetCustomAttributes().Count() > 0) - suites.Add(type); + _suites.Add(type); } public static void RunTests() { GatherTests(); - foreach (var suite in suites) + foreach (var suite in _suites) { - var tests = suite.GetMethods().Where(m => m.GetCustomAttributes().Count() > 0 || m.GetCustomAttributes().Count() > 0).ToArray(); - var setup = suite.GetMethods().Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); - var disposer = suite.GetMethods().Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); + var suiteMethods = suite.GetMethods(); + + var tests = suiteMethods.Where(m => m.GetCustomAttributes().Count() > 0 || m.GetCustomAttributes().Count() > 0).ToArray(); + var setup = suiteMethods.Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); + var disposer = suiteMethods.Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); + var beforeEach = suiteMethods.Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); + var afterEach = suiteMethods.Where(m => m.GetCustomAttributes().Count() > 0).FirstOrDefault(); var instance = Activator.CreateInstance(suite); @@ -71,50 +83,63 @@ public static void RunTests() foreach (var testMethod in tests) { - // Mitigates the AttributeNullException - foreach (var test in testMethod.GetCustomAttributes()) + if (testMethod.GetCustomAttributes().Count() > 0) { bool failed = false; + beforeEach?.Invoke(instance, null); try { testMethod?.Invoke(instance, null); } - catch (Exception e) + catch (TargetInvocationException e) { - if(e.GetType() != typeof(SuccessException)) + if (!(e.InnerException is SuccessException)) failed = true; } + catch (Exception e) + { + failed = true; + } finally { + afterEach?.Invoke(instance, null); Debug.Log($"Test '{suite.Name} {testMethod.Name}' finished with " + (failed ? "Error" : "Success")); } } - - var testCases = testMethod.GetCustomAttributes(); - int successCount = 0; - foreach (var testCase in testCases) + else { - bool failed = false; - try + var testCases = testMethod.GetCustomAttributes(); + int successCount = 0; + foreach (var testCase in testCases) { - var result = testMethod?.Invoke(instance, testCase.Attributes); - if (testCase.ExpectedResult != null) - failed = !testCase.ExpectedResult.Equals(result); - } - catch (Exception e) - { - if(e.GetType() != typeof(SuccessException)) + bool failed = false; + beforeEach?.Invoke(instance, null); + try + { + var result = testMethod?.Invoke(instance, testCase.Attributes); + if (testCase.ExpectedResult != null) + failed = !testCase.ExpectedResult.Equals(result); + } + catch (TargetInvocationException e) + { + if (!(e.InnerException is SuccessException)) + failed = true; + } + catch (Exception e) + { failed = true; + } + finally + { + afterEach?.Invoke(instance, null); + + if (!failed) + successCount++; + } } - finally - { - if (!failed) - successCount++; - } - } - if(testCases.Count() > 0) Debug.Log($"Test '{suite.Name} {testMethod.Name}' finished with {successCount}/{testCases.Count()} successfull test cases."); + } } disposer?.Invoke(instance, null); diff --git a/UnitTests.Editor.csproj b/UnitTests.Editor.csproj index 3ca6c26..3b46309 100644 --- a/UnitTests.Editor.csproj +++ b/UnitTests.Editor.csproj @@ -22,7 +22,7 @@ prompt 4 true - DEBUG;FLAX_ASSERTIONS;TRACE;FLAX_0;FLAX_0_3;FLAX_0_3_6173;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN + DEBUG;FLAX_ASSERTIONS;TRACE;FLAX_0;FLAX_0_5;FLAX_0_5_6187;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN false @@ -33,7 +33,7 @@ prompt 4 true - TRACE;FLAX_0;FLAX_0_3;FLAX_0_3_6173;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN + TRACE;FLAX_0;FLAX_0_5;FLAX_0_5_6187;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN false @@ -42,13 +42,13 @@ - Cache\Assemblies\Release\FlaxEngine.dll + Cache\Assemblies\FlaxEngine.dll - Cache\Assemblies\Release\Newtonsoft.Json.dll + Cache\Assemblies\Newtonsoft.Json.dll - Cache\Assemblies\Release\FlaxEditor.dll + Cache\Assemblies\FlaxEditor.dll @@ -59,6 +59,9 @@ + + + diff --git a/UnitTests.csproj b/UnitTests.csproj index af430b2..94b8756 100644 --- a/UnitTests.csproj +++ b/UnitTests.csproj @@ -22,7 +22,7 @@ prompt 4 true - DEBUG;FLAX_ASSERTIONS;TRACE;FLAX_0;FLAX_0_3;FLAX_0_3_6173;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN + DEBUG;FLAX_ASSERTIONS;TRACE;FLAX_0;FLAX_0_5;FLAX_0_5_6187;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN false @@ -33,7 +33,7 @@ prompt 4 true - TRACE;FLAX_0;FLAX_0_3;FLAX_0_3_6173;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN + TRACE;FLAX_0;FLAX_0_5;FLAX_0_5_6187;FLAX_EDITOR;FLAX_WINDOWS;FLAX_EDITOR_WIN false @@ -42,19 +42,16 @@ - Cache\Assemblies\Release\FlaxEngine.dll + Cache\Assemblies\FlaxEngine.dll - Cache\Assemblies\Release\Newtonsoft.Json.dll + Cache\Assemblies\Newtonsoft.Json.dll - Cache\Assemblies\Release\FlaxEditor.dll + Cache\Assemblies\FlaxEditor.dll - - -