123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using NUnit.Framework;
- using NUnit.Framework.Internal.Commands;
- using NUnit.Framework.Interfaces;
- using NUnit.Framework.Internal;
- using NUnit.Framework.Internal.Builders;
- using UnityEngine.TestRunner.NUnitExtensions.Runner;
-
- namespace UnityEngine.TestTools
- {
- /// <summary>
- /// `UnityTest` attribute is the main addition to the standard [NUnit](http://www.nunit.org/) library for the Unity Test Framework. This type of unit test allows you to skip a frame from within a test (so background tasks can finish) or give certain commands to the Unity **Editor**, such as performing a domain reload or entering **Play Mode** from an **Edit Mode** test.
- /// In Play Mode, the `UnityTest` attribute runs as a [coroutine](https://docs.unity3d.com/Manual/Coroutines.html). Whereas Edit Mode tests run in the [EditorApplication.update](https://docs.unity3d.com/ScriptReference/EditorApplication-update.html) callback loop.
- /// The `UnityTest` attribute is, in fact, an alternative to the `NUnit` [Test attribute](https://github.com/nunit/docs/wiki/Test-Attribute), which allows yielding instructions back to the framework. Once the instruction is complete, the test run continues. If you `yield return null`, you skip a frame. That might be necessary to ensure that some changes do happen on the next iteration of either the `EditorApplication.update` loop or the [game loop](https://docs.unity3d.com/Manual/ExecutionOrder.html).
- /// <example>
- /// ## Edit Mode example
- /// The most simple example of an Edit Mode test could be the one that yields `null` to skip the current frame and then continues to run:
- /// <code>
- /// [UnityTest]
- /// public IEnumerator EditorUtility_WhenExecuted_ReturnsSuccess()
- /// {
- /// var utility = RunEditorUtilityInTheBackground();
- ///
- /// while (utility.isRunning)
- /// {
- /// yield return null;
- /// }
- ///
- /// Assert.IsTrue(utility.isSuccess);
- /// }
- /// </code>
- /// </example>
- /// <example>
- /// ## Play Mode example
- ///
- /// In Play Mode, a test runs as a coroutine attached to a [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html). So all the yield instructions available in coroutines, are also available in your test.
- ///
- /// From a Play Mode test you can use one of Unity’s [Yield Instructions](https://docs.unity3d.com/ScriptReference/YieldInstruction.html):
- ///
- /// - [WaitForFixedUpdate](https://docs.unity3d.com/ScriptReference/WaitForFixedUpdate.html): to ensure changes expected within the next cycle of physics calculations.
- /// - [WaitForSeconds](https://docs.unity3d.com/ScriptReference/WaitForSeconds.html): if you want to pause your test coroutine for a fixed amount of time. Be careful about creating long-running tests.
- ///
- /// The simplest example is to yield to `WaitForFixedUpdate`:
- /// <code>
- /// [UnityTest]
- /// public IEnumerator GameObject_WithRigidBody_WillBeAffectedByPhysics()
- /// {
- /// var go = new GameObject();
- /// go.AddComponent<Rigidbody>();
- /// var originalPosition = go.transform.position.y;
- ///
- /// yield return new WaitForFixedUpdate();
- ///
- /// Assert.AreNotEqual(originalPosition, go.transform.position.y);
- /// }
- /// </code>
- /// </example>
- /// </summary>
- [AttributeUsage(AttributeTargets.Method)]
- public class UnityTestAttribute : CombiningStrategyAttribute, ISimpleTestBuilder, IImplyFixture, ITestBuilder, IApplyToTest
- {
- const string k_MethodMarkedWithUnitytestMustReturnIenumerator = "Method marked with UnityTest must return IEnumerator.";
-
- /// <summary>
- /// Initializes and returns an instance of UnityTestAttribute.
- /// </summary>
- public UnityTestAttribute() : base(new UnityCombinatorialStrategy(), new ParameterDataSourceProvider()) {}
-
- private readonly NUnitTestCaseBuilder _builder = new NUnitTestCaseBuilder();
-
- /// <summary>
- /// This method builds the TestMethod from the Test and the method info. In addition it removes the expected result of the test.
- /// </summary>
- /// <param name="method">The method info.</param>
- /// <param name="suite">The test.</param>
- /// <returns>A TestMethod object</returns>
- TestMethod ISimpleTestBuilder.BuildFrom(IMethodInfo method, Test suite)
- {
- var t = CreateTestMethod(method, suite);
-
- AdaptToUnityTestMethod(t);
-
- return t;
- }
-
- /// <summary>
- /// This method hides the base method from CombiningStrategyAttribute.
- /// It builds a TestMethod from a Parameterized Test and the method info.
- /// In addition it removes the expected result of the test.
- /// </summary>
- /// <param name="method">The method info.</param>
- /// <param name="suite">The test.</param>
- /// <returns>A TestMethod object</returns>
- IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
- {
- var testMethods = base.BuildFrom(method, suite);
-
- foreach (var t in testMethods)
- {
- AdaptToUnityTestMethod(t);
- }
-
- return testMethods;
- }
-
- TestMethod CreateTestMethod(IMethodInfo method, Test suite)
- {
- TestCaseParameters parms = new TestCaseParameters
- {
- ExpectedResult = new object(),
- HasExpectedResult = true
- };
-
- var t = _builder.BuildTestMethod(method, suite, parms);
- return t;
- }
-
- static void AdaptToUnityTestMethod(TestMethod t)
- {
- if (t.parms != null)
- {
- t.parms.HasExpectedResult = false;
- }
- }
-
- static bool IsMethodReturnTypeIEnumerator(IMethodInfo method)
- {
- return !method.ReturnType.IsType(typeof(IEnumerator));
- }
-
- /// <summary>
- /// This method hides the base method ApplyToTest from CombiningStrategyAttribute.
- /// In addition it ensures that the test with the `UnityTestAttribute` has an IEnumerator as return type.
- /// </summary>
- /// <param name="test">The test.</param>
- public new void ApplyToTest(Test test)
- {
- if (IsMethodReturnTypeIEnumerator(test.Method))
- {
- test.RunState = RunState.NotRunnable;
- test.Properties.Set(PropertyNames.SkipReason, k_MethodMarkedWithUnitytestMustReturnIenumerator);
- }
-
- base.ApplyToTest(test);
- }
- }
- }
|