test
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"timestamp": 1719916293,
|
||||
"signature": "DJsA4GYrBqkfMWPIe+Ca29Ovy4OePk4wWSpI+XrtLXd3r7QoFYD80XQUXgbrG6uTY3XZCkNN5vP3XSVTfkPjbHNIgkfiBltCzDHdZLY+bznepmXEpQWGFh3PuipmfGWjDfyLXt+lyMkPHKKZ6Chx6a8K1NR/SQhscyx9U0ibSANcHyd1d5y9sYSlyk7sM/lVUg1BRa3olZhxfLOlKisgD5EPJSNgJfTHotU9rFAtRQzrwpxCe6lqkvk2QGDXcEowkdDtmAbvziJn+rUm1hvLSCr9B/HYeXzXdvmIY2F8BAcCJW4c9HWkwcNuOgKaUVgFi8xhEt2Xjo/xVVglkYLDRSRDj9mIfh6rhQVvXoHiyeoMXO2nQHjF/ep1nTvr2yp7LPrYwhGWWfkYFkpgueHK1/NNhnNItkrBXyZ6mLKD52LtclJbUtL9hGj9yH+bcUgid0d4mIA+mObGzxH6mSRZ+De3I/WCR0xY6mouwHZDhCIULg9POxqc8HFFgcKIY0BT",
|
||||
"publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg"
|
||||
}
|
@@ -0,0 +1,375 @@
|
||||
# Changelog
|
||||
## [1.4.5] - 2024-07-02
|
||||
- Fixed an issue where batchmode test runs would never finish if a test yielded WaitForEndOfFrame (DSTR-1009).
|
||||
- Fixed an issue where the location prompt was required when using the Install all tests in 'build' folder option during test builds.
|
||||
- Canceling a PlayMode test run now correctly restores the scene setup, instead of leaving the editor in the test scene.
|
||||
- Fixed an issue where UnitySetUp did not fail when nested coroutines threw an exception (DSTR-1007).
|
||||
- When selecting multiple tests and running them, the test runner now correctly updates the details of the first selected test (UTF-602).
|
||||
- The interaction mode and application idle time settings is now being changes when running tests, resulting in faster test runs if not already in use (applies to 2020.3 and later) (DSTR-690).
|
||||
- Fixed an issue where some NUnit attributes caused errors and stopped async test runs (DSTR-1040).
|
||||
- Added support for the MaxTime attribute on async and UnityTest methods (DSTR-1040).
|
||||
- Fixed a memory leak issue where a large number of domain reloads within the same test could crash the editor (DSTR-1023).
|
||||
- Changed to use a progress bar inside the test runner window when running tests. This ensures that the progress bar is not fighting to display when e.g. compiling scripts and also makes it easier to cancel a run. This progress bar is displayed for all types of runs (EditMode, PlayMode and Player). (UTF-596).
|
||||
- Fixed an issue where ignored tests with an attributes did not display the ignore reason in the test runner UI.
|
||||
- Having multiple tests with the same unique id no longer gives a error with 'An item with the same key has already been added', but instead logs an warning about the duplicate id.
|
||||
- The result icons for test suites should no longer flicker when running tests.
|
||||
- Ensured that test results ignored in the editor but run on a player are not overwritten with ignore status when shown in the UI (DSTR-1042).
|
||||
- Fixed an issue where the RunStarted event was not invoked correctly during a test run (DSTR-1046).
|
||||
- Fixed an issue where TestStarted and TestFinished events were repeated for ignored test fixtures after a domain reload (DSTR-986).
|
||||
|
||||
## [1.4.4] - 2024-04-11
|
||||
- Fix the issue where playmode controller wasn't being set and cleaned up correctly, causing issues post test run. (DSTR-1025)
|
||||
- Fixed an issue where Oculus Quest headsets might timeout before the test run starts (DSTR-404).
|
||||
- Fixed a regression where the console output would no longer get attached to the test result for PlayMode.
|
||||
|
||||
## [1.4.3] - 2024-01-15
|
||||
- Fixed a performance issue in relation to the new ui, where running a large selection of tests did not perform well. (DSTR-983)
|
||||
- The test tree no longer is fully expanded when the window is first opened, fixing test tree explosion. (DSTR-985)
|
||||
- Fixed an issue in Solution 13 of Samples where the path was pointing to a wrong location. (DSTR-1005)
|
||||
- Updated documentation of Solution 13 of Samples to reflect more accurate solution for the example. (DSTR-1005)
|
||||
- Added ability to build all or selected tests from test runner UI. (UTF-553)
|
||||
|
||||
## [1.4.2] - 2023-12-05
|
||||
- Issue where playmode tests were not recording test results correctly after the first run was fixed. (DSTR-984)
|
||||
- A timeout message is now printed when the test failures occurs before the test finishes. (DSTR-476)
|
||||
- Nested enumerators are now having their first step executed in the same frame as the parent enumerator. (DSTR-888)
|
||||
- Fixed an issue where test projects without PlayMode tests would give the error "No callbacks received." when running in batchmode on some Unity versions.
|
||||
|
||||
## [1.4.1] - 2023-11-13
|
||||
- Multiple improvements to the UI, including better dropdowns, filtering, and a new test list view for Player.
|
||||
- Fixed uncategorized UI tests filtering for parameterized tests (DSTR-219).
|
||||
- In async tests, any failing logs will now first be evaluated after the async method has completed. (DSTR-839)
|
||||
|
||||
## [1.4.0] - 2023-11-10
|
||||
- Added api for saving test results to a file.
|
||||
- Added a button for saving the test results of the latest run.
|
||||
- Applied filtering to the ITestAdaptor argument of `ICallbacks.RunStarted` so that it corresponds to the actual test tree being run.
|
||||
- When running in player:
|
||||
- the save scene dialog is shown when there are changes in the scene.
|
||||
- the scene setup is restored after a completed run.
|
||||
- When running in playmode:
|
||||
- the scene setup is restored after a completed run.
|
||||
- Added overloads of LogAssert.Expect which allow users to expect a log message without specifying a severity, which will match logged messages of any severity.
|
||||
- Added new `[ParametrizedIgnore]` attribute which allows ignoring tests based on arguments which were passed to the test method.
|
||||
- Added a new PreservedValuesAttribute to allow for using the nunit ValuesAttribute at players with a high stripping level (case DSTR-33).
|
||||
- Added api for canceling test runs.
|
||||
|
||||
## [1.3.9] - 2023-08-21
|
||||
- Removed player metadata fields that were using obsolete APIs (DSTR-880).
|
||||
- Added note to documentation on mitigation of problem reported in (DSTR-600).
|
||||
- Fixed an issue where the test runner ui causes progress bars to flicker or not show at all (DSTR-828).
|
||||
|
||||
## [1.3.8] - 2023-07-05
|
||||
- Send new UTP messages regarding player and system settings (DSTR-831)
|
||||
|
||||
## [1.3.7] - 2023-06-07
|
||||
- The UTF version now automatically updates for SRP tests
|
||||
|
||||
## [1.3.6] - 2023-06-01
|
||||
- By using the editor command line new argument `-randomOrderSeed x` you can run the tests in a randomized order, where x is an integer different from 0. If a new test is added in the project the random order passing the same seed will be kept, and the new test will be placed in the random list accordigly.
|
||||
- Fix for WebGL platform target to close the browser tab when the run is completed.
|
||||
- Added TestFileReferences.json to be generated on build step of the player, so can be consumed later by Test runners to enrich data for run part.
|
||||
|
||||
## [1.3.5] - 2023-05-16
|
||||
- It’s now possible to retry and repeat tests on test level, meaning as soon as the test finishs running the first iteration, we now retry or repeat it. Command line arguments to pass to the Editor:
|
||||
- `-repeat x` runs the test x amount of times or until it fails. It is useful for testing unstable tests
|
||||
- `-retry x` if a test fails, run that test x amount of times or until it succeeds.
|
||||
- Fixed various documentation bugs reported via the docs user feedback system.
|
||||
- Added separate reference docs page for TestSettings options to distinguish them from regular command line arguments.
|
||||
- Fixed TestMode not being set correctly on root level of test tree (DSTP-674).
|
||||
- It's now possible to select browser for running WebGL player tests in player settings. (DSTR-811)
|
||||
|
||||
## [1.3.4] - 2023-03-24
|
||||
- Fixes output message concurrency issue with async Setup.
|
||||
- Fixed multiple issues where tests would not time out, when running longer than the default timeout or the timeout defined in a TimeoutAttribute (DSTR-607).
|
||||
- Added `UNITY_TEST_FRAMEWORK` define constraint to filter out test framework assemblies from normal platform and asset bundle builds. (DSTR-791)
|
||||
- Ensured that all playmode tests have a disabled splashscreen and unity logo by default if Unity license permits such action.
|
||||
- Added strictDomainReload feature to enable cheching for pending domain reloads/compilations at the end of managed tests (DSTR-793).
|
||||
|
||||
## [1.3.3] - 2023-02-10
|
||||
- Fixes an issue where a test body would be skipped under certain conditions regarding domain reload.
|
||||
- Fixed an issue where the "uncategorized" category filter would not apply correctly to parameterized tests with a category in the fixture (DSTR-700).
|
||||
- Ensured that all samples can be loaded at once without assembly name collisions.
|
||||
|
||||
## [1.3.2] - 2022-12-07
|
||||
- Fixed context not being restored after a domain reload outside tests (DSTR-678)
|
||||
- Fixed TestMode being set only in on the aseembly level (DSTP-674)
|
||||
- Fixed an issue where RunFinished callbacks sometimes would not be executed before the editor quits in batchmode (DSTR-692).
|
||||
- Fixed problem of samples not loading for import in Package Manager window. (DSTR-702)
|
||||
- Fixed issue GuiHelper depending on FilePath being abosolute. Updated to handle both cases.
|
||||
- Fixed an issue where ITestRunCallback are invoked double when run in EditMode.
|
||||
|
||||
## [1.3.1] - 2022-10-18
|
||||
- Fixed an issue where TestFinished sometimes causes failures when receiving fixture test results from a player (internal).
|
||||
|
||||
## [1.3.0] - 2022-10-11
|
||||
- Fixed Xcode not closing after building iOS/tvOS project via batchmode `-runTests` command (ANT-679).
|
||||
- Added TestSettings file options for setting `Target SDK` for iOS/tvOS (ANT-132).
|
||||
- Async test support with documentation and support for SetUp and TearDown.
|
||||
- Compute and share OneTimeSetup and OneTimeTearDown durations, these will be visible in the XML result under outputs (DSTR-597).
|
||||
- Made test method/fixture arguments available in the ITestAdaptor as the `Arguments` property (DSTR-592).
|
||||
- Added Learn Unity Test Framework section of documentation and related project files as importable package samples (DOCES-558).
|
||||
- Fix NullReferenceException when yielding EditMode intructions in PlayMode tests (DSTR-622).
|
||||
|
||||
## [1.1.33] - 2022-07-12
|
||||
- Fixed an issue where using Assert.Expect with the same string multiple times can lead to incorrect errors in some cases (DSTR-442).
|
||||
- Improved the logging when using multiple Assert.Expect that the logs appear in another order than expected (DSTR-442).
|
||||
- Moved the targetPlatform specified when running tests in the TestRunnerApi from the Filter to the ExecutionSettings (DSTR-186).
|
||||
- Fixed an issue where an inheritance of UnityPlatformAttribute which was not working (ESTT-70).
|
||||
- Fixed the log of excluded platforms which was not displaying the right information.
|
||||
- Added filename and linenumber to test finished message (DSTR-505).
|
||||
- Add the possibility of running tests in a specified order from a test list (DSTR-494).
|
||||
|
||||
## [1.1.32] - 2022-04-06
|
||||
- Ensured that BuildTargetGroup is set correctly before TestPlayerBuildModifier is invoked (DSTR-394).
|
||||
- Added a TestSetting that allows to build an Android App Bundle instead of APK.
|
||||
|
||||
## [1.1.31] - 2022-02-03
|
||||
- Fixed "Open source code" on tests when located inside a package.
|
||||
- Added editor analytics events.
|
||||
- Added `buildPlayerPath` argument. Path to where built player with tests is saved.
|
||||
|
||||
## [1.1.30] - 2021-10-15
|
||||
- Added validation of IEnumerator return type for parameterized tests with UnityTest attribute (DSTP-743).
|
||||
- Fixed runInBackground reset to original value after finishing to run playmode tests (DSTR-248).
|
||||
- Fixed issue with circular assembly references when constructing the test tree (DSTR-300).
|
||||
|
||||
## [1.1.29] - 2021-08-12
|
||||
- Nested enumerator execution order fix (DSTR-227).
|
||||
- Fix UI not running any tests if run select on a nested namespaces (DSTR-256).
|
||||
|
||||
## [1.1.28] - 2021-06-25
|
||||
- Fix CountDownEvent reference due to `com.unity.ext.nunit` update.
|
||||
- Various performance optimization to fix "Test execution timed out. No activity received from the player in 600 seconds."(DSTR-100).
|
||||
|
||||
## [1.1.27] - 2021-06-15
|
||||
- Fix empty reason on passed tests results xml (DSTR-63)
|
||||
- Fix Repeat and Retry attribute for UnityTest in PlayMode (DSTR-237).
|
||||
- Remove XDK Xbox One platform after Unity 2020.3
|
||||
- Fixed issue when `.` suffix was applied to BuildTargets without extension.
|
||||
- Added support for `GameCoreXboxOne` and `GameCoreXboxSeries` reduced location path length.
|
||||
|
||||
## [1.1.26] - 2021-05-25
|
||||
- Fix html bug in TestRunnerApi API code snippet (DS-1973).
|
||||
- Fix typo bug in PreBuildSetup code example (DS-1974).
|
||||
- Fix incorrect syntax in command line reference (DS-1971).
|
||||
- Fixed a bug where test filter would match project or player path (DSTP-412).
|
||||
- Added playerGraphicsAPI TestSettings parameter
|
||||
|
||||
## [1.1.25] - 2021-05-05
|
||||
- Fixed a bug where test filter would match project or player path (DSTP-412).
|
||||
- Added playerGraphicsAPI TestSettings parameter
|
||||
|
||||
## [1.1.24] - 2021-03-04
|
||||
- Improving UTF documentation(DSTR-120)
|
||||
- Updated "Actions outside of tests" section of user manual. Added flow charts to clarify execution order for SetUp/TearDown, TestActions, and complete flow (DSTR-121).
|
||||
- Fixed accepted values for scriptingBackend argument to be string literals instead of int values (DSTR-122).
|
||||
- Fixed possible values of ResultState to be Passed, Failed, Skipped, Inconclusive, plus labels instead of Success and Failure (DSTR-125).
|
||||
- Added NUNit version information (DSTR-130).
|
||||
- Added namespace information for LogAsset in user manual (DSTR-124).
|
||||
- Added instructions for creating additional sets of tests (DSTR-129).
|
||||
- Added information on testResults XML output format and exit codes (DSTR-131).
|
||||
- Updated description of testPlatform command line argument to clarify accepted values and their meaning (DSTR-123).
|
||||
- Reduce time taken by filtering operations when only a subset of tests is run.
|
||||
- Reduced the time taken to rebuild the test tree and to scan for assets a test created but did not delete.
|
||||
- Reduce the per-test overhead of running tests in the editor.
|
||||
- Added profiler markers around test setup, teardown, and execution.
|
||||
- Fixed unstable timeout bug (DSTR-21).
|
||||
|
||||
## [1.1.23] - 2021-01-21
|
||||
- Improving UTF documentation(DSTR-120)
|
||||
- Updated "Actions outside of tests" section of user manual. Added flow charts to clarify execution order for SetUp/TearDown, TestActions, and complete flow (DSTR-121).
|
||||
- Fixed accepted values for scriptingBackend argument to be string literals instead of int values (DSTR-122).
|
||||
- Fixed possible values of ResultState to be Passed, Failed, Skipped, Inconclusive, plus labels instead of Success and Failure (DSTR-125).
|
||||
- Added NUNit version information (DSTR-130).
|
||||
- Added namespace information for LogAsset in user manual (DSTR-124).
|
||||
- Added instructions for creating additional sets of tests (DSTR-129).
|
||||
- Added information on testResults XML output format and exit codes (DSTR-131).
|
||||
- Updated description of testPlatform command line argument to clarify accepted values and their meaning (DSTR-123).
|
||||
|
||||
## [1.1.22] - 2021-01-21
|
||||
- Fixed issue where test result of an explicit test was set to skipped in case it was passing and running from command line with testfilter set to the explicit test (DS-1236).
|
||||
- Fixed an issue where tests located in assemblies that did not directly reference any test assemblies were not included (DSTR-30).
|
||||
- Fixed an issue where UnitySetup methods were incorrectly being rerun when entering playmode, rather than being skipped (DSTR-68).
|
||||
- Internal: Remove ##utp message AssemblyCompilationErrors (DS-1277)
|
||||
- Fixed issue where if the timeout was exceeded in SetUp the timeout exception was not thrown(DSTR-21).
|
||||
- Removed ability to `Enable playmode tests for all assemblies` from the TestRunner UI, since it is a deprecated behavior. It enforces to use of assembly definition files (DSTR-45).
|
||||
- Fixed typo in `LogAssert.cs` documentation.
|
||||
|
||||
## [1.1.21] - 2020-12-04
|
||||
- Fixed issue where test result of an explicit test was set to skipped in case it was passing and running from command line with testfilter set to the explicit test (DS-1236).
|
||||
- Fixed an issue where tests located in assemblies that did not directly reference any test assemblies were not included (DSTR-30).
|
||||
- Fixed an issue where UnitySetup methods were incorrectly being rerun when entering playmode, rather than being skipped (DSTR-68).
|
||||
- Internal: Remove ##utp message AssemblyCompilationErrors (ds-1277)
|
||||
- Fixed issue where if the timeout was exceeded in SetUp the timeout exception was not thrown(DSTR-21).
|
||||
- Removed ability to `Enable playmode tests for all assemblies` from the TestRunner UI, since it is a deprecated behavior. It enforces to use of assembly definition files (DSTR-45).
|
||||
|
||||
## [1.1.20] - 2020-12-04
|
||||
- The logscope is now available in OneTimeTearDown.
|
||||
- Fixed an issue where failing tests would not result in the correct exit code if a domain reload happens after the test has run (DS-1304).
|
||||
- If a player build fails, the test specific build settings should be cleaned up and the original values restored as intended (DS-1001).
|
||||
- Added better error message when using TestRunCallbackAttribute and the implementation is stripped away (DS-454).
|
||||
- Fixed an issue where the test results xml would have a zero end-time for tests executed before a domain reload (DSTR-63).
|
||||
- Fixed OpenSource in case of a Test in a nested class (DSTR-6)
|
||||
- UnityTests with a domain reload now works correctly in combination with Retry and Repeat attributes (DS-428).
|
||||
- Fixed OpenSource in case of Tests located inside a package (DS-432)
|
||||
|
||||
## [1.1.19] - 2020-11-17
|
||||
- Command line runs with an inconclusive test result now exit with exit code 2 (case DS-951).
|
||||
- Fixed timeout during UnitySetUp which caoused test to pass instead of failing due to wrong time format.
|
||||
- Timeout exeption thrown when timeout time is exeded in the UnitySetup when using `WaitForSeconds(n)`.
|
||||
- Updating `com.unity.ext.nunit` version
|
||||
- Method marked with UnityTest that are not returning IEnumerator is now giving a proper error (DS-1059).
|
||||
|
||||
## [1.1.18] - 2020-10-07
|
||||
- Fixed issue of timeout during UnitySetUp which wasn't detected and allowed the test to pass instead of failing (case DSTR-21)
|
||||
|
||||
## [1.1.17] - 2020-10-05
|
||||
- Fixed an issue where the WaitForDomainReload yield instruction would sometimes let the test continue for one frame before the domain reload.
|
||||
- Added support for negation in filters using !. E.g. !CategoryToExclude.
|
||||
- Fixed an issue where if the first test enters PlayMode from UnitySetup then the test body will not run on consecutive runs (case 1260901).
|
||||
- Clear Results button clears the test results in the GUI (DSTR-16)
|
||||
- Improved UI in Test Runner window, added new options:
|
||||
- Run Selected Tests in player
|
||||
- Build/Export project with all tests in player
|
||||
- Build/Export project with selected tests in player
|
||||
- Fixed issue on loading EditMode or Playmode test tree in the wrong tab when switching between tabs when TestRunner is loading (DS-865)
|
||||
|
||||
## [1.1.16] - 2020-07-09
|
||||
- Follow up on fix when UTF picks up on outdated compilation errors
|
||||
|
||||
## [1.1.15] - 2020-07-02
|
||||
- Fixed an issue where an exception is thrown on getting the enumerator of a UnityTest would result in stopping the test run instead of failing it (case 1212000).
|
||||
- Including a trailing semi-colon in a testName filter no longer results in all tests being run (case 1171200).
|
||||
- Fixed and issue when Unity Test Framework exits editor on an outdated script compilation error (during api updates)
|
||||
|
||||
## [1.1.14] - 2020-04-03
|
||||
- Added the 'assemblyNames' command line argument for filtering on the assembly level.
|
||||
- The dll and project level of the tree view should now correctly show the results when running tests in a player (case 1197026).
|
||||
- Optimize usage of player connection when transfering test results (case 1229200).
|
||||
- Ignore internal test framework tests assertions (case 1206961).
|
||||
|
||||
## [1.1.13] - 2020-03-16
|
||||
- Fixed an issue where a combination of Entering / Exiting playmode and recompiling scripts would result in the test run repeating (case 1213958).
|
||||
- Fixed a regression from 1.1.12 where prefabs left in the scene would be cleaned up to aggressively.
|
||||
- Fixed Test execution timed out. No activity received from the player in 600 seconds error when player is not supposed to start (case 1225147)
|
||||
|
||||
## [1.1.12] - 2020-03-02
|
||||
- Now 'Open error line' for a failed UTF test does not throw exceptions for corrupted testable pdb in Editor release mode (case 1118259)
|
||||
- Fixed an issue where running a test fixture would also run other fixtures with the same full name (namespace plus classname) in other assemblies (case 1197385).
|
||||
- Running tests with the same full name, with a domain reload inbetween, will no longer fail to initialize the fixture of the second class (case 1205240).
|
||||
- Running a playmode tests with "Maximize on Play" will now correctly show the result of the tests in the test runner window (case 1014908).
|
||||
- Fixed an issue where leaving a game object in a scene with a DontSaveInEditor hideFlags would result in an error on cleanup (case 1136883).
|
||||
- Now ITestPlayerBuildModifier.ModifyOptions is called as expected when running tests on a device (case 1213845)
|
||||
|
||||
## [1.1.11] - 2020-01-16
|
||||
- Fixed test runner dlls got included into player build (case 1211624)
|
||||
- Passing a non-full-path of XML file for -testResults in Unity Batchmode issue resolved, now passing "result.xml" creates the result file in the project file directory (case 959078)
|
||||
- Respect Script Debugging build setting when running tests
|
||||
|
||||
## [1.1.10] - 2019-12-19
|
||||
- Introduced PostSuccessfulLaunchAction callback
|
||||
- Fixed an issue where canceling a UnityTest while it was running would incorrectly mark it as passed instead of canceled.
|
||||
- Added command line argument for running tests synchronously.
|
||||
- The test search bar now handles null values correctly.
|
||||
- The test output pane now retains its size on domain reloads.
|
||||
|
||||
## [1.1.9] - 2019-12-12
|
||||
- Rolled back refactoring to the test run system, as it caused issues in some corner cases.
|
||||
|
||||
## [1.1.8] - 2019-11-15
|
||||
- Ensured that a resumed test run is continued instantly.
|
||||
|
||||
## [1.1.7] - 2019-11-14
|
||||
- Fixed an issue with test runs after domain reload.
|
||||
|
||||
## [1.1.6] - 2019-11-12
|
||||
- Building a player for test will no longer look in unrelated assemblies for relevant attributes.
|
||||
|
||||
## [1.1.5] - 2019-10-23
|
||||
- Fixed a regression to synchronous runs introduced in 1.1.4.
|
||||
|
||||
## [1.1.4] - 2019-10-15
|
||||
- Running tests in batch mode now correctly returns error code 3 (RunError) when a timeout or a build error occurs.
|
||||
- Fixed an issue where a test run in a player would time out, if the player takes longer than 10 minutes to run.
|
||||
- Added command line argument and api setting for specifying custom heartbeat timeout for running on players.
|
||||
|
||||
## [1.1.3] - 2019-09-23
|
||||
- Fixed a regression where tests in a player would report a timeout after a test run is finished.
|
||||
- Made it possible for the ui to change its test items when the test tree changes without script compilation.
|
||||
- Added synchronous runs as an option to the TestRunnerApi.
|
||||
|
||||
## [1.1.2] - 2019-09-11
|
||||
- Fixed an issue where Run Selected would run all tests in the category, if a category filter was selected, regardless of what tests were selected.
|
||||
- Unsupported attributes used in UnityTests now give an explicit error.
|
||||
- Added support for the Repeat and Retry attributes in UnityTests (case 1131940).
|
||||
- Tests with a explicit timeout higher than 10 minutes, no longer times out after running longer than 10 minutes when running from command line (case 1125991).
|
||||
- Fixed a performance regression in the test runner api result reporting, introduced in 2018.3 (case 1109865).
|
||||
- Fixed an issue where parameterized test fixtures would not run if selected in the test tree (case 1092244).
|
||||
- Pressing Clear Results now also correctly clears the counters on the test list (case 1181763).
|
||||
- Prebuild setup now handles errors logged with Debug.LogError and stops the run if any is logged (case 1115240). It now also supports LogAssert.Expect.
|
||||
|
||||
## [1.1.1] - 2019-08-07
|
||||
- Tests retrieved as a test list with the test runner api incorrectly showed both mode as their TestMode.
|
||||
- Fixed a compatibility issue with running tests from rider.
|
||||
|
||||
## [1.1.0] - 2019-07-30
|
||||
- Introduced the TestRunnerApi for running tests programmatically from elsewhere inside the Editor.
|
||||
- Introduced yield instructions for recompiling scripts and awaiting a domain reload in Edit Mode tests.
|
||||
- Added a button to the Test Runner UI for clearing the results.
|
||||
|
||||
## [1.0.18] - 2019-07-15
|
||||
- Included new full documentation of the test framework.
|
||||
|
||||
## [1.0.17] - 2019-07-11
|
||||
- Fixed an issue where the Test Runner window wouldn’t frame selected items after search filter is cleared.
|
||||
- Fixed a regression where playmode test application on the IOS platform would not quit after the tests are done.
|
||||
|
||||
## [1.0.16] - 2019-06-20
|
||||
- Fixed an issue where the Test Runner window popped out if it was docked, or if something else was docked next to it, when re-opened (case 1158961)
|
||||
- Fixed a regression where the running standalone playmode tests from the ui would result in an error.
|
||||
|
||||
## [1.0.15] - 2019-06-18
|
||||
- Added new `[TestMustExpectAllLogs]` attribute, which automatically does `LogAssert.NoUnexpectedReceived()` at the end of affected tests. See docs for this attribute for more info on usage.
|
||||
- Fixed a regression where no tests would be run if multiple filters are specified. E.g. selecting both a whole assembly and an individual test in the ui.
|
||||
- Fixed an issue where performing `Run Selected` on a selected assembly would run all assemblies.
|
||||
- Introduced the capability to do a split build and run, when running playmode tests on standalone devices.
|
||||
- Fixed an error in ConditionalIgnore, if the condition were not set.
|
||||
|
||||
## [1.0.14] - 2019-05-27
|
||||
- Fixed issue preventing scene creation in IPrebuildSetup.Setup callback when running standalone playmode tests.
|
||||
- Fixed an issue where test assemblies would sometimes not be ordered alphabetically.
|
||||
- Added module references to the package for the required modules: imgui and jsonserialize.
|
||||
- Added a ConditionalIgnore attribute to help ignoring tests only under specific conditions.
|
||||
- Fixed a typo in the player test window (case 1148671).
|
||||
|
||||
## [1.0.13] - 2019-05-07
|
||||
- Fixed a regression where results from the player would no longer update correctly in the UI (case 1151147).
|
||||
|
||||
## [1.0.12] - 2019-04-16
|
||||
- Added specific unity release to the package information.
|
||||
|
||||
## [1.0.11] - 2019-04-10
|
||||
- Fixed a regression from 1.0.10 where test-started events were triggered multiple times after a domain reload.
|
||||
|
||||
## [1.0.10] - 2019-04-08
|
||||
- Fixed an issue where test-started events would not be fired correctly after a test performing a domain reload (case 1141530).
|
||||
- The UI should correctly run tests inside a nested class, when that class is selected.
|
||||
- All actions should now correctly display a prefix when reporting test result. E.g. "TearDown :".
|
||||
- Errors logged with Debug.LogError in TearDowns now append the error, rather than overwriting the existing result (case 1114306).
|
||||
- Incorrect implementations of IWrapTestMethod and IWrapSetUpTearDown now gives a meaningful error.
|
||||
- Fixed a regression where the Test Framework would run TearDown in a base class before the inheriting class (case 1142553).
|
||||
- Fixed a regression introduced in 1.0.9 where tests with the Explicit attribute could no longer be executed.
|
||||
|
||||
## [1.0.9] - 2019-03-27
|
||||
- Fixed an issue where a corrupt instance of the test runner window would block for a new being opened.
|
||||
- Added the required modules to the list of package requirements.
|
||||
- Fixed an issue where errors would happen if the test filter ui was clicked before the ui is done loading.
|
||||
- Fix selecting items with duplicate names in test hierarchy of Test Runner window (case 987587).
|
||||
- Fixed RecompileScripts instruction which we use in tests (case 1128994).
|
||||
- Fixed an issue where using multiple filters on tests would sometimes give an incorrect result.
|
||||
|
||||
## [1.0.7] - 2019-03-12
|
||||
### This is the first release of *Unity Package com.unity.test-framework*.
|
||||
|
||||
- Migrated the test-framework from the current extension in unity.
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d691174143fd3774ba63d7c493633b99
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
# Contributing
|
||||
|
||||
## If you are interested in contributing, here are some ground rules:
|
||||
* ... Define guidelines & rules for what contributors need to know to successfully make Pull requests against your repo ...
|
||||
|
||||
## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/licenses/Unity_Contribution_Agreement)
|
||||
By making a pull request, you are confirming agreement to the terms and conditions of the UCA, including that your Contributions are your original creation and that you have complete right and authority to make your Contributions.
|
||||
|
||||
## Once you have a change ready following these ground rules. Simply make a pull request
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57d2ac5c7d5786e499d4794973fe0d4e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,99 @@
|
||||
* [Unity Test Framework overview](./index.md)
|
||||
* [What's new](./whats-new.md)
|
||||
* [Edit Mode vs. Play Mode tests](./edit-mode-vs-play-mode-tests.md)
|
||||
* [Getting started with UTF](./getting-started.md)
|
||||
* [How to create a new test assembly](./workflow-create-test-assembly.md)
|
||||
* [How to create a test](./workflow-create-test.md)
|
||||
* [How to run a test](./workflow-run-test.md)
|
||||
* [How to run a Play Mode test as standalone](./workflow-run-playmode-test-standalone.md)
|
||||
* [Extending UTF](./extending.md)
|
||||
* [How to split the build and run process for standalone Play Mode tests](./reference-attribute-testplayerbuildmodifier.md#split-build-and-run-for-player-mode-tests)
|
||||
* [How to run tests programmatically](./extension-run-tests.md)
|
||||
* [How to get test results](./extension-get-test-results.md)
|
||||
* [How to retrieve the list of tests](./extension-retrieve-test-list.md)
|
||||
* [Reference](./manual.md#reference)
|
||||
* [Running tests from the command-line](./reference-command-line.md)
|
||||
* [Test settings file](./reference-test-settings-file.md)
|
||||
* [UnityTest attribute](./reference-attribute-unitytest.md)
|
||||
* [Setup and cleanup at build time](./reference-setup-and-cleanup.md)
|
||||
* [IPrebuildSetup](./reference-setup-and-cleanup.md#iprebuildsetup)
|
||||
* [IPostBuildCleanup](./reference-setup-and-cleanup.md#ipostbuildcleanup)
|
||||
* [Actions outside of tests](./reference-actions-outside-tests.md)
|
||||
* [Action execution order](./reference-actions-outside-tests.md#action-execution-order)
|
||||
* [UnitySetUp and UnityTearDown](./reference-unitysetup-and-unityteardown.md)
|
||||
* [OuterUnityTestAction](./reference-outerunitytestaction.md)
|
||||
* [Domain Reloads](./reference-actions-outside-tests.md#domain-reloads)
|
||||
* [Custom attributes](./reference-custom-attributes.md)
|
||||
* [ConditionalIgnore attribute](./reference-attribute-conditionalignore.md)
|
||||
* [ParameterizedIgnore attribute](./reference-attribute-parameterizedignore.md)
|
||||
* [PostBuildCleanup attribute](./reference-setup-and-cleanup.md#prebuildsetup-and-postbuildcleanup)
|
||||
* [PrebuildSetup attribute](./reference-setup-and-cleanup.md#prebuildsetup-and-postbuildcleanup)
|
||||
* [TestMustExpectAllLogs attribute](./reference-attribute-testmustexpectalllogs.md)
|
||||
* [TestPlayerBuildModifier attribute](./reference-attribute-testplayerbuildmodifier.md)
|
||||
* [TestRunCallback attribute](./reference-attribute-testruncallback.md)
|
||||
* [UnityPlatform attribute](./reference-attribute-unityplatform.md)
|
||||
* [UnitySetUp attribute](./reference-unitysetup-and-unityteardown.md)
|
||||
* [UnityTearDown attribute](./reference-unitysetup-and-unityteardown.md)
|
||||
* [UnityTest attribute](./reference-attribute-unitytest.md)
|
||||
* [Custom equality comparers](./reference-custom-equality-comparers.md)
|
||||
* [ColorEqualityComparer](./reference-comparer-color.md)
|
||||
* [FloatEqualityComparer](./reference-comparer-float.md)
|
||||
* [QuaternionEqualityComparer](./reference-comparer-quaternion.md)
|
||||
* [Vector2EqualityComparer](./reference-comparer-vector2.md)
|
||||
* [Vector3EqualityComparer](./reference-comparer-vector3.md)
|
||||
* [Vector4EqualityComparer](./reference-comparer-vector4.md)
|
||||
* [Custom equality comparers with equals operator](./reference-comparer-equals.md)
|
||||
* [Test Utils](./reference-test-utils.md)
|
||||
* [Custom yield instructions](./reference-custom-yield-instructions.md)
|
||||
* [IEditModeTestYieldInstruction](./reference-custom-yield-instructions.md#IEditModeTestYieldInstruction)
|
||||
* [EnterPlayMode](./reference-custom-yield-instructions.md#enterplaymode)
|
||||
* [ExitPlayMode](./reference-custom-yield-instructions.md#exitplaymode)
|
||||
* [RecompileScripts](./reference-recompile-scripts.md)
|
||||
* [WaitForDomainReload](./reference-wait-for-domain-reload.md)
|
||||
* [Custom assertion](./reference-custom-assertion.md)
|
||||
* [LogAssert](./reference-custom-assertion.md#logassert)
|
||||
* [Custom constraints](./reference-custom-constraints.md)
|
||||
* [Is](./reference-custom-constraints.md#is)
|
||||
* [Parameterized tests](./reference-tests-parameterized.md)
|
||||
* [MonoBehaviour tests](./reference-tests-monobehaviour.md)
|
||||
* [MonoBehaviourTest<T>](./reference-tests-monobehaviour.md#monobehaviourtestt)
|
||||
* [IMonoBehaviourTest](./reference-tests-monobehaviour.md#imonobehaviourtest)
|
||||
* [TestRunnerApi](./reference-test-runner-api.md)
|
||||
* [ExecutionSettings](./reference-execution-settings.md)
|
||||
* [Filter](./reference-filter.md)
|
||||
* [ITestRunSettings](./reference-itest-run-settings.md)
|
||||
* [ICallbacks](./reference-icallbacks.md)
|
||||
* [IErrorCallbacks](./reference-ierror-callbacks.md)
|
||||
* [Async tests](./reference-async-tests.md)
|
||||
* Learn Unity Test Framework
|
||||
* [Overview](./course/overview.md)
|
||||
* General introduction
|
||||
* [Intro](./course/welcome.md)
|
||||
* [Running a test in a Unity project](./course/running-test.md)
|
||||
* [Arrange, act, assert](./course/arrange-act-assert.md)
|
||||
* [Semantic test assertion](./course/semantic-test-assertion.md)
|
||||
* [Custom comparison](./course/custom-comparison.md)
|
||||
* [Asserting logs](./course/asserting-logs.md)
|
||||
* [Setup and teardown](./course/setup-teardown.md)
|
||||
* [Play mode tests](./course/play-mode-tests.md)
|
||||
* [Play mode tests in a player](./course/play-mode-tests-in-player.md)
|
||||
* [Using the UnityTest attribute](./course/unitytest-attribute.md)
|
||||
* [Long-running tests](./course/long-running-tests.md)
|
||||
* [Scene-based tests](./course/scene-based-tests.md)
|
||||
* [Setup and cleanup at build time](./course/build-setup-cleanup.md)
|
||||
* [Domain reload](./course/domain-reload.md)
|
||||
* [Preserve test state](./course/preserve-test-state.md)
|
||||
* [Test cases](./course/test-cases.md)
|
||||
* [Custom attributes](./course/custom-attributes.md)
|
||||
* [Running tests programmatically](./course/running-tests-programmatically.md)
|
||||
* Testing Lost Crypt
|
||||
* [Intro](./course/LostCrypt/welcome.md)
|
||||
* [Setting up](./course/LostCrypt/setting-up.md)
|
||||
* [Running a test in LostCrypt](./course/LostCrypt/first-test.md)
|
||||
* [Moving character](./course/LostCrypt/moving-character.md)
|
||||
* [Reach wand test](./course/LostCrypt/reach-wand-test.md)
|
||||
* [Collision test](./course/LostCrypt/collision-test.md)
|
||||
* [Asset change test](./course/LostCrypt/asset-change-test.md)
|
||||
* [Scene validation test](./course/LostCrypt/scene-validation-test.md)
|
||||
* [Performance tests](./course/LostCrypt/performance-tests.md)
|
||||
* [Resources](./resources.md)
|
@@ -0,0 +1,113 @@
|
||||
# 6\. Asset Change Test
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
This exercise will teach you a popular pattern in Game Tests to verify if Assets change over time.
|
||||
|
||||
## Exercise
|
||||
|
||||
As you noticed inside LostCrypt, when you pick up the Wand, your character equips armor.
|
||||
Write a test that checks that after Sara picks up the wand, armor is equipped.
|
||||
|
||||
1. Create a `WandTests.cs` class and implement `MainScene\_CharacterReachesWandAndEquipsArmor` test.
|
||||
2. Try to observe how `Sara Variant` or more specifically `puppet_sara` GameObject changes the moment you pick up the wand.
|
||||
|
||||
## Hints
|
||||
|
||||
* You can reuse code from [Reach Wand Test](./reach-wand-test.md) for the logic of the character picking up the wand. Or you can try to trigger this action programmatically.
|
||||
* Remember that if some Unity internal APIs are not accessible for your test you might need to add a new reference inside the `PlayModeTests` assembly definition.
|
||||
|
||||
## Solution
|
||||
|
||||
PlayModeTests.asmdef
|
||||
```
|
||||
{
|
||||
"name": "PlayModeTests",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.InputSystem",
|
||||
"Unity.InputSystem.TestFramework",
|
||||
"TestInputControl",
|
||||
"UnityEngine.TestRunner",
|
||||
"Unity.2D.Animation.Runtime"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
```
|
||||
|
||||
WandTests.cs
|
||||
```
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.Experimental.U2D.Animation;
|
||||
|
||||
public class WandTests
|
||||
{
|
||||
private Transform _characterTransform;
|
||||
private float _testTimeout = 25.0f;
|
||||
private float _wandLocation = 21.080f;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MainScene_CharacterReachesWandAndEquipsArmor()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
|
||||
// Skip first frame so Sara have a chance to appear on the screen
|
||||
yield return null;
|
||||
var puppet = GameObject.Find("puppet_sara");
|
||||
var spriteLibrary = puppet.GetComponent<SpriteLibrary>();
|
||||
|
||||
Assert.AreEqual(spriteLibrary.spriteLibraryAsset.name, "Sara");
|
||||
|
||||
var elapsedTime = 0.0f;
|
||||
yield return GoRight();
|
||||
while (GetCurrentCharacterPosition() <= _wandLocation)
|
||||
{
|
||||
yield return null;
|
||||
elapsedTime += Time.deltaTime;
|
||||
if (elapsedTime > _testTimeout)
|
||||
{
|
||||
Assert.Fail($"Character did not reach location position in {_testTimeout} seconds.");
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for Wand pickup animation to be over.
|
||||
yield return new WaitForSeconds(12);
|
||||
|
||||
Assert.AreEqual(spriteLibrary.spriteLibraryAsset.name, "Sara_var01");
|
||||
}
|
||||
|
||||
private float GetCurrentCharacterPosition()
|
||||
{
|
||||
// Get Main character's Transform which is used to manipulate position.
|
||||
if (_characterTransform == null)
|
||||
{
|
||||
_characterTransform = GameObject.Find("Sara Variant").transform;
|
||||
}
|
||||
|
||||
return _characterTransform.position.x;
|
||||
}
|
||||
|
||||
private IEnumerator GoRight()
|
||||
{
|
||||
TestInputControl.MoveLeft = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveRight = true;
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,93 @@
|
||||
# 5\. Collision Test
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
Check for collisions and make sure that LostCrypt does not have bugs that allow your character to move outside of the map.
|
||||
|
||||
## Exercise
|
||||
|
||||
Take a look at a game object `Environment/Character Bounds - Left`. You can see that it is placed at the left side of our 2D map. It is meant to protect players from exiting the map and falling into textures. Let's see if it fulfills its purpose.
|
||||
|
||||
1. Add a new test `MainScene\_CharacterDoesNotFallIntoTextures` in `MovementTest.cs`.
|
||||
2. Make your character move left and occasionally jump with some wait interval in between jumps.
|
||||
3. In test, Assert that _Sara Variant_ game object position is within bounds of our current scene.
|
||||
|
||||
|
||||
## Hints
|
||||
|
||||
* Similarly to the previous test, let's set some arbitrary amount of seconds as our timeout. Sara should stay within the bounds of the scene for the given time.
|
||||
* You might want to use `WaitForSeconds(0.5f)` between jumps to emulate User behaviour better.
|
||||
* Study the Scene and hardcode X, and Y position used for out of map check, or better - get it dynamically from `Character Bounds - Left` game object.
|
||||
|
||||
## Solution
|
||||
|
||||
MovementTest.cs
|
||||
```
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class MovementTest
|
||||
{
|
||||
const float _testTimeout = 20.0f;
|
||||
private Transform _characterTransform;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MainScene_CharacterDoesNotFallIntoTextures()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
yield return waitForSceneLoad();
|
||||
|
||||
yield return GoLeft();
|
||||
while (Time.timeSinceLevelLoad < _testTimeout)
|
||||
{
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
yield return Jump();
|
||||
if (GetCurrentCharacterPosition().x < -75f && GetCurrentCharacterPosition().y < -10f)
|
||||
{
|
||||
Assert.Fail("Character escaped the map and fell into textures! :(");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetCurrentCharacterPosition()
|
||||
{
|
||||
// Get Main character's Transform which is used to manipulate position.
|
||||
if (_characterTransform == null)
|
||||
{
|
||||
_characterTransform = GameObject.Find("Sara Variant").transform;
|
||||
}
|
||||
|
||||
return _characterTransform.position;
|
||||
}
|
||||
|
||||
private IEnumerator Jump()
|
||||
{
|
||||
TestInputControl.Jump = true;
|
||||
yield return null;
|
||||
TestInputControl.Jump = false;
|
||||
}
|
||||
|
||||
private IEnumerator GoLeft()
|
||||
{
|
||||
TestInputControl.MoveRight = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveLeft = true;
|
||||
}
|
||||
|
||||
private IEnumerator waitForSceneLoad()
|
||||
{
|
||||
while (SceneManager.GetActiveScene().buildIndex > 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Our test fails, we have a bug in one of our Sample Unity projects. How would you approach fixing this problem? There are plenty of possibilities, go ahead and try to fix it as part of this training:
|
||||
|
||||
* Introduce new Character Bounds Box collider that will prevent the bug from happening.
|
||||
* Rework our Sara character collision logic.
|
@@ -0,0 +1,46 @@
|
||||
# 2\. Running a test in a LostCrypt
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
Set up a simple Play Mode test for LostCrypt.
|
||||
|
||||
## Exercise
|
||||
|
||||
1. Go to the `Assets/Scripts` directory, and spend some time exploring the scripts necessary for LostCrypt to work properly.
|
||||
2. Create a new directory `Assets/Tests`.
|
||||
3. In the Test Runner window click **Create PlayModeTest Assembly Folder** and name a new folder `PlayModeTests`. You should end up with `Assets/Tests/PlayModeTests`.
|
||||
4. Open the newly created folder and click **Create Test Script in current folder** in the Test Runner window.
|
||||
5. Name the file `SceneSetupTests.cs`.
|
||||
6. Write your first test that asserts that after loading the Main scene the current time is day.
|
||||
|
||||
## Hints
|
||||
|
||||
* In order to load scenes, please refer to [UnityEngine.SceneManagement](https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.html) documentation.
|
||||
* Inside `Scenes/Main.unity` [look for GameObject](https://docs.unity3d.com/ScriptReference/GameObject.Find.html) **FX - Day**.
|
||||
|
||||
## Solution
|
||||
|
||||
SceneSetupTests.cs
|
||||
|
||||
```
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class SceneSetupTests
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator MainScene_LoadsCorrectlyAndItsDaytime()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
yield return null;
|
||||
|
||||
var fxDay = GameObject.Find("FX - Day");
|
||||
|
||||
Assert.IsTrue(fxDay != null, "should find the 'FX - Day' object in the scene");
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,134 @@
|
||||
# 3\. Moving character
|
||||
|
||||
## Learning objectives
|
||||
|
||||
How to use the Unity InputSystem package to have a generic way of moving your character programmatically in tests.
|
||||
|
||||
## Exercise
|
||||
|
||||
Please make sure [InputSystem](https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/QuickStartGuide.html) is installed in your Unity project. You can verify that by checking the Package Manager.
|
||||
|
||||
1. Create a new class called `MovementTest.cs` under `Assets/Tests/PlayModeTests`.
|
||||
2. Attach the reference to `Unity.InputSystem` and `Unity.InputSystem.TestFramework` in your `PlayModeTests` assembly definition.
|
||||
3. Create a new `InputControl` directory under Tests: `Assets/Tests/InputControl`.
|
||||
4. Inside `InputControl` directory, create a new assembly definition: `TestInputControl.asmdef`.
|
||||
5. Create a new class `TestInputControl.cs` where you implement following properties:
|
||||
```
|
||||
public static bool MoveLeft { get; set; }
|
||||
public static bool MoveRight { get; set; }
|
||||
public static bool Jump { get; set; }
|
||||
```
|
||||
6. Go back to your assembly definition `PlayModeTests` and attach the reference to newly created: `TestInputControl`.
|
||||
7. Finally, we need to use our `TestInputControl` in actual LostCrypt code. Currently Unity's `InputSystem` does not support an easier way of programmatically doing mocks, please see this git diff to know what to change inside `CharacterController2D`:
|
||||
|
||||
```
|
||||
diff --git a/Assets/Scripts/CharacterController2D.cs b/Assets/Scripts/CharacterController2D.cs
|
||||
index f8a10cf2..e0a62878 100644
|
||||
--- a/Assets/Scripts/CharacterController2D.cs
|
||||
+++ b/Assets/Scripts/CharacterController2D.cs
|
||||
@@ -81,15 +81,15 @@ public class CharacterController2D : MonoBehaviour
|
||||
// Horizontal movement
|
||||
float moveHorizontal = 0.0f;
|
||||
|
||||
- if (keyboard.leftArrowKey.isPressed || keyboard.aKey.isPressed)
|
||||
+ if (keyboard.leftArrowKey.isPressed || keyboard.aKey.isPressed || TestInputControl.MoveLeft)
|
||||
moveHorizontal = -1.0f;
|
||||
- else if (keyboard.rightArrowKey.isPressed || keyboard.dKey.isPressed)
|
||||
+ else if (keyboard.rightArrowKey.isPressed || keyboard.dKey.isPressed || TestInputControl.MoveRight)
|
||||
moveHorizontal = 1.0f;
|
||||
|
||||
movementInput = new Vector2(moveHorizontal, 0);
|
||||
|
||||
// Jumping input
|
||||
- if (!isJumping && keyboard.spaceKey.wasPressedThisFrame)
|
||||
+ if (!isJumping && (keyboard.spaceKey.wasPressedThisFrame || TestInputControl.Jump))
|
||||
jumpInput = true;
|
||||
}
|
||||
```
|
||||
|
||||
Now you are ready! Go back to `MovementTest.cs` and write a test that does not do any assertions (just yet), but only moves the Sara character and makes it occasionally jump.
|
||||
|
||||
## Hints
|
||||
|
||||
* You might want to use `WaitForSeconds` in your test, to deliberately make it run longer and see actual animation happening on your screen.
|
||||
* In case of compilation issues, please make sure you follow the right folder structure:
|
||||
|
||||
```
|
||||
Tests
|
||||
InputControl
|
||||
TestInputControl.asmdef
|
||||
TestInputControl.cs
|
||||
PlayModeTests
|
||||
MovementTest.cs
|
||||
PlayModeTest.asmdef
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
PlayModeTests.asmdef
|
||||
```
|
||||
{
|
||||
"name": "PlayModeTests",
|
||||
"references": [
|
||||
"Unity.InputSystem",
|
||||
"Unity.InputSystem.TestFramework",
|
||||
"TestInputControl"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
]
|
||||
}
|
||||
```
|
||||
MovementTest.cs
|
||||
```
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class MovementTest
|
||||
{
|
||||
[UnityTest]
|
||||
public IEnumerator MainScene_CharacterIsAbleToJump()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
yield return waitForSceneLoad();
|
||||
yield return GoRight();
|
||||
yield return new WaitForSeconds(2);
|
||||
yield return Jump();
|
||||
yield return new WaitForSeconds(3);
|
||||
yield return GoLeft();
|
||||
yield return Jump();
|
||||
yield return new WaitForSeconds(2);
|
||||
}
|
||||
|
||||
private IEnumerator Jump()
|
||||
{
|
||||
TestInputControl.Jump = true;
|
||||
yield return null;
|
||||
TestInputControl.Jump = false;
|
||||
}
|
||||
|
||||
private IEnumerator GoRight()
|
||||
{
|
||||
TestInputControl.MoveLeft = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveRight = true;
|
||||
}
|
||||
|
||||
private IEnumerator GoLeft()
|
||||
{
|
||||
TestInputControl.MoveRight = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveLeft = true;
|
||||
}
|
||||
|
||||
private IEnumerator waitForSceneLoad()
|
||||
{
|
||||
while (SceneManager.GetActiveScene().buildIndex > 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,95 @@
|
||||
# 8\. Performance Tests
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
One final thing we'll explore is a package that extends Unity Test Framework with Performance Tests.
|
||||
|
||||
## Exercise
|
||||
|
||||
The Performance Testing package can be used to measure performance in our game. This is a great tool if we want to track various regressions/progressions that happen over time in our project. In this example, you'll learn how to create a test that measures game average frames.
|
||||
|
||||
1. LostCrypt does not include the Performance Testing package installed by default. Install it by following [these instructions](https://docs.unity3d.com/Packages/com.unity.test-framework.performance@2.8/manual/index.html).
|
||||
2. Add the package as a dependency to the [project manifest](https://docs.unity3d.com/Manual/upm-manifestPrj.html).
|
||||
3. When the package is installed, add a reference to `Unity.PerformanceTesting` in your **PlayModeTests** assembly definition to access the performance testing APIs.
|
||||
4. Create a new C# class under **Assets/Tests/PlayModeTests** called **PerformanceTests.cs**.
|
||||
|
||||
You're now ready to complete your objective. In `PerformanceTests.cs` create a new function called `MainScene_MeasureAverageFrames()`. In this function move your character to the wand position and wait until the wand pickup effect is over. During all that time, measure the frames.
|
||||
|
||||
## Bonus
|
||||
|
||||
* Try to measure the average FPS in LostCrypt. You might need to use `Time.deltaTime` from UnityEngine API and `Measure.Custom` from the Performance Testing package API.
|
||||
|
||||
## Hints
|
||||
|
||||
* The first handful of frames after loading Scene are usually unstable, let's utilize the `Measure.Frames().Scope()` API to measure them into a separate scope.
|
||||
* After your test finishes, performance results can be viewed under **Window > Analysis > Performance Test Report** or you can even hook into results using [Callback API](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-get-test-results.html).
|
||||
|
||||
## Solution
|
||||
|
||||
PerformanceTests.cs
|
||||
```
|
||||
using System.Collections;
|
||||
using Unity.PerformanceTesting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class PerformanceTests
|
||||
{
|
||||
private Transform _characterTransform;
|
||||
private float _wandLocation = 21.080f;
|
||||
|
||||
[UnityTest, Performance]
|
||||
public IEnumerator MainScene_MeasureAverageFrames()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
using (Measure.Frames().Scope("Frames.MainSceneOnLoad.Unstable"))
|
||||
{
|
||||
for (var i = 0; i < 25; i++)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
using (Measure.Frames().Scope("Frames.MainSceneGameplay"))
|
||||
{
|
||||
yield return GoRight();
|
||||
while (GetCurrentCharacterPosition() <= _wandLocation)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
StopMoving();
|
||||
yield return new WaitForSeconds(15);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetCurrentCharacterPosition()
|
||||
{
|
||||
// Get Main character's Transform which is used to manipulate position.
|
||||
if (_characterTransform == null)
|
||||
{
|
||||
_characterTransform = GameObject.Find("Sara Variant").transform;
|
||||
}
|
||||
|
||||
return _characterTransform.position.x;
|
||||
}
|
||||
|
||||
private IEnumerator GoRight()
|
||||
{
|
||||
TestInputControl.MoveLeft = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveRight = true;
|
||||
}
|
||||
|
||||
private void StopMoving()
|
||||
{
|
||||
TestInputControl.MoveRight = false;
|
||||
TestInputControl.MoveLeft = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Bonus Solution
|
||||
|
||||
`Measure.Custom("FPS", (int)(1f / Time.deltaTime));`
|
@@ -0,0 +1,78 @@
|
||||
# 4\. Reach Wand Test
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
Perform Assertions on your character position and behavior.
|
||||
|
||||
## Exercise
|
||||
|
||||
1. Go back to the previous `MovementTest.cs` file.
|
||||
2. Write a `MainScene\_CharacterReachesWand` test that makes your character move right, and checks if it reaches the wand location.
|
||||
|
||||
## Hints
|
||||
|
||||
* Look for _Altar_ and _Sara Variant_ game objects in your scene. You are interested in measuring the X position of your Transform objects.
|
||||
* Wand location X position is equal float of **21.080**. Main Character X position is dynamic and it changes whenever it moves.
|
||||
* Consider setting a timeout that makes the test fail if the Wand is not reached.
|
||||
|
||||
## Solution
|
||||
|
||||
```
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class MovementTest
|
||||
{
|
||||
private Transform _characterTransform;
|
||||
private float _testTimeout = 25.0f;
|
||||
private float _wandLocation = 21.080f;
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator MainScene_CharacterReachesWand()
|
||||
{
|
||||
SceneManager.LoadScene("Assets/Scenes/Main.unity", LoadSceneMode.Single);
|
||||
yield return waitForSceneLoad();
|
||||
|
||||
var elapsedTime = 0.0f;
|
||||
yield return GoRight();
|
||||
while (GetCurrentCharacterPosition() <= _wandLocation)
|
||||
{
|
||||
yield return null;
|
||||
elapsedTime += Time.deltaTime;
|
||||
if (elapsedTime > _testTimeout)
|
||||
{
|
||||
Assert.Fail($"Character did not reach location position in {_testTimeout} seconds.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float GetCurrentCharacterPosition()
|
||||
{
|
||||
// Get Main character's Transform which is used to manipulate position.
|
||||
if (_characterTransform == null)
|
||||
{
|
||||
_characterTransform = GameObject.Find("Sara Variant").transform;
|
||||
}
|
||||
|
||||
return _characterTransform.position.x;
|
||||
}
|
||||
|
||||
private IEnumerator GoRight()
|
||||
{
|
||||
TestInputControl.MoveLeft = false;
|
||||
yield return null;
|
||||
TestInputControl.MoveRight = true;
|
||||
}
|
||||
|
||||
private IEnumerator waitForSceneLoad()
|
||||
{
|
||||
while (SceneManager.GetActiveScene().buildIndex > 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,92 @@
|
||||
# 7\. Scene Validation Test
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
Test scene for presence of Sara and Wand game object. Utilize Test Framework feature to make this test use all scenes as fixtures.
|
||||
|
||||
## Exercise
|
||||
|
||||
1. Create **ValidationTest.cs** file with a single namespace and two classes _SceneValidationTests_ and _GameplayScenesProvider_.
|
||||
2. In the Tests class create **SaraAndWandArePresent** test to check that "Sara Variant" and "Wand" game objects are not null.
|
||||
3. In the Fixture class `GameplayScenesProvider` implement `IEnumerable<string>` and in generator method yield all scenes from [EditorBuildSettings.scenes](https://docs.unity3d.com/ScriptReference/EditorBuildSettings-scenes.html).
|
||||
4. Use `TestFixture` and [TestFixtureSource](https://docs.nunit.org/articles/nunit/writing-tests/attributes/testfixturesource.html) annotations on _SceneValidationTests_ class.
|
||||
5. Create a new Empty Scene and attach it to `EditorBuildSettings` to verify if tests are created dynamically.
|
||||
|
||||
## Hints
|
||||
|
||||
* `TestFixture` and `TestFixtureSource` NUnit annotations require Test Class to be present inside Namespace.
|
||||
* To attach a scene to `EditorBuildSettings`, you need to create a new Scene, and then add it to **File > Build Settings**.
|
||||
|
||||
## Solution
|
||||
|
||||
ValidationTests.cs
|
||||
```
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace ValidationTests
|
||||
{
|
||||
[TestFixture]
|
||||
[TestFixtureSource(typeof(GameplayScenesProvider))]
|
||||
public class SceneValidationTests
|
||||
{
|
||||
private readonly string _scenePath;
|
||||
|
||||
public SceneValidationTests(string scenePath)
|
||||
{
|
||||
_scenePath = scenePath;
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void LoadScene()
|
||||
{
|
||||
SceneManager.LoadScene(_scenePath);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator SaraAndWandArePresent()
|
||||
{
|
||||
yield return waitForSceneLoad();
|
||||
var wand = GameObject.Find("Wand");
|
||||
var sara = GameObject.Find("Sara Variant");
|
||||
|
||||
Assert.NotNull(wand, "Wand object exists");
|
||||
Assert.NotNull(sara, "Sara object exists");
|
||||
}
|
||||
|
||||
IEnumerator waitForSceneLoad()
|
||||
{
|
||||
while (!SceneManager.GetActiveScene().isLoaded)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GameplayScenesProvider : IEnumerable
|
||||
{
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
foreach (var scene in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (!scene.enabled || scene.path == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return scene.path;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,34 @@
|
||||
# 1\. Setting up LostCrypt
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
In this exercise you'll set up a simple Unity 2D project and import a sample project (LostCrypt).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Unity 2020.3 LTS** - recommended version of Unity for this training session
|
||||
2. **C# IDE** (for example [Rider](https://www.jetbrains.com/rider/download/) or [Visual Studio](https://visualstudio.microsoft.com)) - not necessary but highly recommended. This way you can use features like a debugger and reliable syntax autocompletion.
|
||||
|
||||
## Exercise
|
||||
|
||||
1. Open **Unity Hub** and click _New Project_. Select a blank 2D (or Core2D) project.
|
||||
2. Enter a **Project Name** and click **Create**.
|
||||
3. Visit the [LostCrypt](https://assetstore.unity.com/packages/essentials/tutorial-projects/lost-crypt-2d-sample-project-158673) asset page. Click _Add to my Assets_ -> _Open in Unity Editor_.
|
||||
4. **Package Manager** window opens automatically. Find **Lost Crypt - 2D Sample Project**. Press _Download_ and then _Import_.
|
||||
5. **Import Unity Package** window opens. Click _Import_ to add all additional packages and assets to your newly created project.
|
||||
6. Restart Unity if needed.
|
||||
|
||||
Now confirm that LostCrypt works correctly.
|
||||
|
||||
1. From the **Project** tab open `Scenes/Main`.
|
||||
2. Enter Play Mode by clicking Play button.
|
||||
3. You should be able to move your character around.
|
||||
|
||||
## Further reading and Resources
|
||||
|
||||
You can read more about LostCrypt in [our blog post](https://www.google.com/url?q=https://blog.unity.com/technology/download-our-new-2d-sample-project-lost-crypt&source=gmail-html&ust=1653726008832000&usg=AOvVaw2RORHgX1nn7hE7KZW3e_lA).
|
||||
|
||||
## Hints (what can go wrong)
|
||||
|
||||
* There might be some dependency problems - please make sure LostCrypt is downloaded for the suggested Unity LTS version.
|
||||
* Make sure you have the newest project packages in your Package Manager.
|
@@ -0,0 +1,24 @@
|
||||
# Testing Lost Crypt
|
||||
|
||||
Welcome to the this training material for the Unity Test Framework (UTF).
|
||||
|
||||
The training is structured with a selection of exercises, starting with more basic topics and then expanding on that knowledge.
|
||||
|
||||
Each section has a Learning Objectives section, which can help you pick what exercises will teach you new things. The exercises are grouped thematically and their difficulty varies.
|
||||
|
||||
**This course focus on testing an actual game. Our candidate is the [LostCrypt](https://assetstore.unity.com/packages/essentials/tutorial-projects/lost-crypt-2d-sample-project-158673) example project.**
|
||||
|
||||
## Course outline
|
||||
|
||||
0. [Setting up](./setting-up.md)
|
||||
1. [Running a test in LostCrypt](./first-test.md)
|
||||
2. [Moving character](./moving-character.md)
|
||||
3. [Reach wand test](./reach-wand-test.md)
|
||||
4. [Collision test](./collision-test.md)
|
||||
5. [Asset change test](./asset-change-test.md)
|
||||
6. [Scene validation test](./scene-validation-test.md)
|
||||
7. [Performance tests](./performance-tests.md)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Although not technically a requirement, we strongly recommend you complete the [General Introduction](../welcome.md) course before attempting this one.
|
@@ -0,0 +1,75 @@
|
||||
# 2\. Arrange, Act, Assert
|
||||
|
||||
## Learning Objectives
|
||||
|
||||
In this exercise, you will learn about the core unit testing principle of AAA (Arrange, Act, Assert), which will help you structure your unit test.
|
||||
|
||||
## Intro and Motivation
|
||||
|
||||
The Arrange, Act, Assert concept is an industry standard in unit testing. It allows for a clear distinction of the code for setting up your test, carrying out the test, and evaluation. Using this can make your test more readable both for yourself and for your colleagues.
|
||||
|
||||
In the first part of the code, we arrange all the elements needed for the test. In the middle part, we act on the object that is under test. In the final part, we assert on the result of the act part. The three parts of the code are usually separated by an empty line.
|
||||
|
||||
An example of Arrange, Act, Assert could look like the following:
|
||||
```
|
||||
[Test]
|
||||
public void StringWriterTest()
|
||||
{
|
||||
// Arrange
|
||||
var stringWriterUnderTest = new StringWriter();
|
||||
stringWriterUnderTest.NewLine = "\\n";
|
||||
var testStringA = "I am testing";
|
||||
var testStringB = "with new line";
|
||||
|
||||
// Act
|
||||
stringWriterUnderTest.WriteLine(testStringA);
|
||||
stringWriterUnderTest.WriteLine(testStringB);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("I am testing\\nwith new line\\n", stringWriterUnderTest.ToString());
|
||||
}
|
||||
```
|
||||
|
||||
It is good practice to use `XUnderTest` as a variable name of the class that is being tested. This helps to keep the focus of the test clean.
|
||||
|
||||
The Act part of the code should have as few lines as possible, reflecting what is actually being tested. The assert should in the optimal case only contain assert calls, but it can also be necessary to include some lines of logic to allow for the assertion.
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `2_ActArrangeAssert` into your Unity Editor (version 2019.2 or newer) from the Package Manager window.
|
||||
|
||||
In this project we have a class called `StringFormatter`. It has two methods of interest: `void Configure(string joinDelimiter)` and `string Join(object[] args)`.
|
||||
|
||||
The goal of this exercise is to write one or more tests, testing the `Join` method. For example, testing that it can join with a ";" (semicolon) delimiter.
|
||||
|
||||
## Hints
|
||||
|
||||
* Setup of the test input and the call to `Configure(";")` would go into the `Arrange` part of your test.
|
||||
* It is good practice to separate the three parts of your test (arrange, act and assert) with a blank line.
|
||||
|
||||
## Solution
|
||||
|
||||
The exercise can be solved with a test like the following:
|
||||
|
||||
```
|
||||
[Test]
|
||||
public void JoinsObjectsWithSemiColon()
|
||||
{
|
||||
// Arrange
|
||||
var formatterUnderTest = new StringFormatter();
|
||||
formatterUnderTest.Configure(";");
|
||||
var objects = new object[] {"a", "bc", 5, "d"};
|
||||
|
||||
// Act
|
||||
var result = formatterUnderTest.Join(objects);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual("a;bc;5;d", result);
|
||||
}
|
||||
```
|
||||
|
||||
A full project with the solution can be found in the sample `2_ActArrangeAssert_Solution.`
|
||||
|
||||
## Further reading and Resources
|
||||
|
||||
The AAA concept is a widely used standard which you can read more about in many online sources, including [this blogpost.](https://defragdev.com/blog/?p=783)
|
@@ -0,0 +1,80 @@
|
||||
# 5\. Asserting and expecting logs
|
||||
|
||||
## Learning objectives
|
||||
|
||||
How to test and verify code that writes to the console log.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
At Unity, we have many packages and modules that communicate with the user through logging messages and exceptions to the console log. This can be both for the normal workflows and for error cases.
|
||||
|
||||
We have extended the test framework to be aware of the console log. This means that by default, any error or exception that is logged while running a test will result in that test failing. If such failures are expected, then it is possible to use `LogAssert.Expect(logtype, message)` to ensure that a given message is logged. This can be used to expect normal messages and warnings as well. The `LogAssert.Expect` can be placed both before and after the message happens. When the test is done (or next time it yields), it will fail if the expected message is not present.
|
||||
|
||||
```
|
||||
[Test]
|
||||
public void LogAssertExample()
|
||||
{
|
||||
// Expect a regular log message
|
||||
LogAssert.Expect(LogType.Log, "Log message");
|
||||
|
||||
// The test fails without the following expected log message
|
||||
Debug.Log("Log message");
|
||||
|
||||
// An error log
|
||||
Debug.LogError("Error message");
|
||||
|
||||
// Without expecting an error log, the test would fail
|
||||
LogAssert.Expect(LogType.Error, "Error message");
|
||||
}
|
||||
```
|
||||
|
||||
The `LogAssert.Expect` also takes a regex as an argument, as sometimes it is not possible to know the precise string. For example, if the logged message has time duration in the string.
|
||||
|
||||
## Exercise
|
||||
|
||||
In the [sample](./welcome.md#import-samples) `5_AssertingLogs` there is a class called `MyLoggingClass`.
|
||||
|
||||
The class has two methods with the following behavior:
|
||||
|
||||
* `DoSomething();` logs the message "Doing something".
|
||||
|
||||
* `DoSomethingElse();` logs an error "An error happened. Code: #" where # is a random number from 0 to 9.
|
||||
|
||||
Write tests that verify the above behavior using `LogAssert.Expect`. You can experiment by seeing what happens if `DoSomethingElse();` is called without the expect and what happens if you expect e.g. a message of type warning.
|
||||
|
||||
## Hints
|
||||
|
||||
* You will need to use a regular expression together with `LogAssert.Expect` in order to expect the error message.
|
||||
* In Unity, there is a difference between a logged error and a logged exception.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution to the exercise can be found in the sample `5_AssertingLogs_Solution`.
|
||||
|
||||
One possible implementation of the tests is as follows:
|
||||
|
||||
```
|
||||
[Test]
|
||||
public void DoSomethingLogsMessage()
|
||||
{
|
||||
var loggingClassUnderTest = new MyLoggingClass();
|
||||
|
||||
loggingClassUnderTest.DoSomething();
|
||||
|
||||
LogAssert.Expect(LogType.Log, "Doing something");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoSomethingElseLogsError()
|
||||
{
|
||||
var loggingClassUnderTest = new MyLoggingClass();
|
||||
|
||||
loggingClassUnderTest.DoSomethingElse();
|
||||
|
||||
LogAssert.Expect(LogType.Error, new Regex("An error happened. Code: \\d"));
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Documentation for LogAssert](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-custom-assertion.html#logassert)
|
@@ -0,0 +1,125 @@
|
||||
# 12\. Setup and cleanup at build time
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will introduce you to the hooks in the test framework for before and after the player build.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
Sometimes it's necessary to change settings or prepare assets before a build for Play Mode tests. Similarly, it might be relevant to clean up things after the build. For this the test framework has two hookup points called [PrebuildSetup and PostBuildCleanup](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-setup-and-cleanup.html).
|
||||
|
||||
In the Editor, the `PrebuildSetup` is invoked before the build and test run and the `PostBuildCleanup` is invoked after the tests are completely done. This happens for both Edit Mode and Play Mode tests. When running Play Mode tests on a device, the Cleanup is already run right after the build is done, as the tests are happening in parallel on the device.
|
||||
|
||||
The simplest way of ensuring a test has a `PrebuildSetup` and `PostBuildCleanup` is by implementing `IPrebuildSetup` and `IPostBuildCleanup` respectively in your test class.
|
||||
|
||||
Often the setup and cleanup will be interacting with code in the `UnityEditor` assemblies. These are not available when running on a device, but we want our built-in setup and cleanup code to stay in the test class. For this, it's recommended to wrap the Editor-related code lines in `#if UNITY_EDITOR` defines. For example:
|
||||
|
||||
```
|
||||
public class MyTestClass : IPrebuildSetup
|
||||
{
|
||||
[Test]
|
||||
public void MyTest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorSettings.serializationMode = SerializationMode.ForceText;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> Note: If the Editor code is not wrapped, then you won't see any compilation error when running in the Editor, but you will see the compilation error once you try to run the test in a player.
|
||||
|
||||
## Exercise
|
||||
|
||||
The sample `12_BuildSetupCleanup` contains a Play Mode test for verifying the content of a scene. It is essentially the Play Mode version of the test from the previous exercise.
|
||||
|
||||
The test fails because the scene can't be found. It could be solved by adding the scene to the build settings, but it's not good practise to add a test-related scene to the build settings, as it could get included when building for non-testing purposes.
|
||||
|
||||
Therefore the task is to create a `PrebuildSetup` that adds the scene to `EditorBuildSettings` and a `PostBuildCleanup` that removes it again.
|
||||
|
||||
Test the solution by running the test both in the Editor and in a standalone player. You will need to use `#if UNITY_EDITOR` to make the code compile for the player.
|
||||
|
||||
## Hints
|
||||
|
||||
* The `IPrebuildSetup` interface requires a `Setup` method, so be careful that there are no `[SetUp]` methods already called that.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution is available in the [sample](./welcome.md#import-samples) `12_BuildSetupCleanup_Solution`.
|
||||
|
||||
The full test solution can be done like this:
|
||||
|
||||
```
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
public class SceneTests : IPrebuildSetup, IPostBuildCleanup
|
||||
{
|
||||
private string originalScene;
|
||||
private const string k_SceneName = "Assets/MyGameScene.unity";
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (EditorBuildSettings.scenes.Any(scene => scene.path == k_SceneName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var includedScenes = EditorBuildSettings.scenes.ToList();
|
||||
includedScenes.Add(new EditorBuildSettingsScene(k_SceneName, true));
|
||||
EditorBuildSettings.scenes = includedScenes.ToArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
[UnitySetUp]
|
||||
public IEnumerator SetupBeforeTest()
|
||||
{
|
||||
originalScene = SceneManager.GetActiveScene().path;
|
||||
SceneManager.LoadScene(k_SceneName);
|
||||
yield return null; // Skip a frame
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyScene()
|
||||
{
|
||||
var gameObject = GameObject.Find("GameObjectToTestFor");
|
||||
|
||||
Assert.That(gameObject, Is.Not.Null, $"GameObjectToTestFor not found in {SceneManager.GetActiveScene().path}.");
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TeardownAfterTest()
|
||||
{
|
||||
SceneManager.LoadScene(originalScene);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EditorBuildSettings.scenes = EditorBuildSettings.scenes.Where(scene => scene.path != k_SceneName).ToArray();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that `#if UNITY_EDITOR` is also used among the using statements, to allow for a using reference to `UnityEditor`.
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[UTF documentation for PrebuildSetup and PostBuildCleanup](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-setup-and-cleanup.html).
|
@@ -0,0 +1,93 @@
|
||||
# 16\. Custom attributes
|
||||
|
||||
## Learning objectives
|
||||
|
||||
In this section we will look at some ways of implementing custom NUnit attributes, which can be used to alter test execution.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
A powerful part of NUnit is that it is very extendable. One of the ways it can be extended is through custom attributes. An example is attributes that implement the [IWrapTestMethod](https://docs.nunit.org/articles/nunit/extending-nunit/ICommandWrapper-Interface.html) interface. This interface has a method for wrapping a `TestCommand`, which implements a method for executing. Normally these test commands do something, call execute on their inner command and then maybe do something again after the inner command is completed.
|
||||
|
||||
In the following three classes an `IWrapTestMethod` interface is implemented and used in a test:
|
||||
|
||||
```
|
||||
public class MyAttribute : NUnitAttribute, IWrapTestMethod
|
||||
{
|
||||
public TestCommand Wrap(TestCommand command)
|
||||
{
|
||||
return new MyCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
public class MyCommand : TestCommand
|
||||
{
|
||||
private TestCommand innerCommand;
|
||||
|
||||
public MyCommand(TestCommand command) : base(command.Test)
|
||||
{
|
||||
innerCommand = command;
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
Debug.Log("Before");
|
||||
var result = innerCommand.Execute(context);
|
||||
Debug.Log("After");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class MyTests
|
||||
{
|
||||
[Test]
|
||||
[MyAttribute]
|
||||
public void Test1()
|
||||
{
|
||||
Debug.Log("The test");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When running `MyTests.Test1` the following output is printed:
|
||||
|
||||
Test1 (0,017s)
|
||||
\---
|
||||
Before
|
||||
The test
|
||||
After
|
||||
|
||||
Other interfaces that custom attributes can implement are `IWrapSetUpTearDown`, `IApplyToContext`, and `IApplyToTest`.
|
||||
|
||||
## Exercise
|
||||
|
||||
At Unity we have a goal that an action should never take longer than 500 ms. In the [sample](./welcome.md#import-samples) `16_CustomAttributes` there is a class called `MyClass`, which has two methods. Both methods are supposed to return true. However someone has made a regression so that one of the two methods takes a long time to run.
|
||||
|
||||
The task is to create a new custom attribute, which detects if the test takes longer than 500 ms to run. If that happens, it should fail the test with a descriptive message. Apply that to the two existing tests.
|
||||
|
||||
## Hints
|
||||
|
||||
* You can use the class `System.Diagnostics.Stopwatch` to time how many miliseconds have passed.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution for the exercise is availiable at `16_CustomAttributes_Solution`.
|
||||
|
||||
The core of the solution is the execute method in the test command implementation:
|
||||
|
||||
```
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
var result = innerCommand.Execute(context);
|
||||
stopWatch.Stop();
|
||||
|
||||
if (stopWatch.ElapsedMilliseconds > 500)
|
||||
{
|
||||
result.SetResult(ResultState.Failure, $"Test took {stopWatch.ElapsedMilliseconds} ms. That is longer than 500ms!");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
@@ -0,0 +1,85 @@
|
||||
# 4\. Custom comparison
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This exercise will cover the custom equality comparers included in Unity Test Framework, such as `Vector3EqualityComparer`. These are used to assert on e.g. Vectors.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
We have extended the assertion capabilities of NUnit with some custom comparisons for Unity-specific objects. A good example of this is the ability to compare two `Vector3` objects.
|
||||
|
||||
An example of its use is:
|
||||
|
||||
```
|
||||
actual = new Vector3(0.01f, 0.01f, 0f);
|
||||
expected = new Vector3(0.01f, 0.01f, 0f);
|
||||
|
||||
Assert.That(actual, Is.EqualTo(expected).Using(Vector3EqualityComparer.Instance));
|
||||
```
|
||||
|
||||
This allows us to verify that the two vectors are identical within a given tolerence. By default the tolerance is 0.0001f. The tolerance can be changed by providing a new `Vector3EqualityComparer`, instead of using the default in .instance. For example you can up the tolerance to 0.01f with the following:
|
||||
|
||||
```
|
||||
Assert.That(actual, Is.EqualTo(expected).Using(new Vector3EqualityComparer(0.01f));
|
||||
```
|
||||
|
||||
For a list of all available custom comparers, see [Custom equality comparers](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-custom-equality-comparers.html).
|
||||
|
||||
## Exercise
|
||||
|
||||
Similar to the project for exercise 3, the [sample](./welcome.md#import-samples) `4_CustomComparison` contains a `ValueOutputter` class.
|
||||
|
||||
Verify that the `ValueOutputter` returns the correct values from its methods:
|
||||
|
||||
* `GetVector3()` should return a `Vector3` that is roughly equal to (10.333, 3, 9.666).
|
||||
|
||||
* `GetFloat()` should return a `float` that is roughly 19.333. This is the same as previous exercise, but you can try to solve this with a `FloatEqualityComparer`.
|
||||
|
||||
* `GetQuaternion` should return a [Quaternion](https://docs.unity3d.com/ScriptReference/Quaternion.html) object that should be roughly equal to (10f, 0f, 7.33333f, 0f).
|
||||
|
||||
## Hints
|
||||
|
||||
* For some of the exercises, you might need to provide a custom error tolerance to the comparer.
|
||||
* If the comparison fails, the comparers give a message about the actual and expected value, just like a normal assertion. However, because `ToString` on `Vector3` rounds the value off before displaying it, the two values in the string message might be equal, even when their `Vector3` values are not.
|
||||
|
||||
## Solution
|
||||
|
||||
The full solution is available in the sample `4_CustomComparison_Solution`.
|
||||
|
||||
```
|
||||
[Test]
|
||||
public void Vector3ReturnsCorrectValue()
|
||||
{
|
||||
var valueOutputterUnderTest = new ValueOutputter();
|
||||
|
||||
var vector3 = valueOutputterUnderTest.GetVector3();
|
||||
|
||||
var expected = new Vector3(10.333f, 3f, 9.666f);
|
||||
Assert.That(vector3, Is.EqualTo(expected).Using(new Vector3EqualityComparer(0.001f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FloatReturnsCorrectValue()
|
||||
{
|
||||
var valueOutputterUnderTest = new ValueOutputter();
|
||||
|
||||
var actualFloat = valueOutputterUnderTest.GetFloat();
|
||||
|
||||
Assert.That(actualFloat, Is.EqualTo(19.333f).Using(new FloatEqualityComparer(0.001f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QuaternionReturnsCorrectValue()
|
||||
{
|
||||
var valueOutputterUnderTest = new ValueOutputter();
|
||||
|
||||
var actualValue = valueOutputterUnderTest.GetQuaternion();
|
||||
|
||||
var expectedValue = new Quaternion(10f, 0f, 7.33333f, 0f);
|
||||
Assert.That(actualValue, Is.EqualTo(expectedValue).Using(new QuaternionEqualityComparer(0.001f)));
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Custom equality comparers](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-custom-equality-comparers.html)
|
@@ -0,0 +1,98 @@
|
||||
# 13\. Domain reload
|
||||
|
||||
## Learning objectives
|
||||
|
||||
In this section, you will learn how to invoke and wait for Domain Reloads.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
When performing actions that affect the scripts in a project, Unity performs a domain reload. Since a domain reload restarts all scripts, then it's necessary to mark any expected domain reload by yielding a [WaitForDomainReload](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-wait-for-domain-reload.html). The command stops any further code execution and then resumes after the domain reload is done.
|
||||
|
||||
It's also possible to yield a [RecompileScripts](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-recompile-scripts.html) command. This does the same as `WaitForDomainReload` except that it performs an `AssetDatabase.Reload()` call. Both calls can be configured to expect whether a script compilation is expected to succeed.
|
||||
|
||||
If a domain reload happens while a test is running without yielding one of these commands, then the test will fail with an error about an unexpected domain reload.
|
||||
|
||||
## Exercise
|
||||
|
||||
The [sample](./welcome.md#import-samples) `13_DomainReload_Solution` is set up with a test class called `ScriptAddingTests`.
|
||||
|
||||
The test has two helper methods already implemented:
|
||||
|
||||
* `CreateScript` creates a C# script with a class called `MyTempScript`. That has a method called `Verify`.
|
||||
|
||||
* `VerifyScript` instantiates an instance of `MyTempScript` using reflection and returns the value from the `Verify` method. The expected return value is the string "OK".
|
||||
|
||||
After running `CreateScript` Unity now has a new C# file in the project and thus needs to recompile. The task is to create a test that calls `CreateScript`, handles the domain reload and then verifies the output from `VerifyScript`.
|
||||
|
||||
Remember that your script should also clean up after itself, by deleting the file and recompiling the script again. This is recommended to do in a `TearDown` or `UnityTearDown`, which will run even if the test fails.
|
||||
|
||||
> **Important**: After importing, you should **move the sample test folder** `Tests_13` into the `Assets` folder for this exercise to work.
|
||||
|
||||
## Hints
|
||||
|
||||
* If `RecompileScripts` is unavailable to you due to it being internal, then you need to upgrade the Unity Test Framework package to version 1.1.0 or higher.
|
||||
* If you are on a non-Windows machine you might want to change paths inside **k\_fileName** or use C# [Path.Combine](https://docs.microsoft.com/en-us/dotnet/api/system.io.path.combine?view=net-6.0) for more cross-platform safe code.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution is available in the sample `13_DomainReload_Solution`.
|
||||
|
||||
The test can be implemented as follows:
|
||||
|
||||
```
|
||||
internal class ScriptAddingTests
|
||||
{
|
||||
private const string k_fileName = @"Assets\\Tests\\TempScript.cs";
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator CreatedScriptIsVerified()
|
||||
{
|
||||
CreateScript();
|
||||
yield return new RecompileScripts();
|
||||
|
||||
var verification = VerifyScript();
|
||||
|
||||
Assert.That(verification, Is.EqualTo("OK"));
|
||||
}
|
||||
|
||||
[UnityTearDown]
|
||||
public IEnumerator Teardown()
|
||||
{
|
||||
if (!File.Exists(k_fileName))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
File.Delete(k_fileName);
|
||||
yield return new RecompileScripts();
|
||||
}
|
||||
|
||||
private void CreateScript()
|
||||
{
|
||||
File.WriteAllText(k_fileName, @"
|
||||
public class MyTempScript {
|
||||
public string Verify()
|
||||
{
|
||||
return ""OK"";
|
||||
}
|
||||
}");
|
||||
}
|
||||
|
||||
private string VerifyScript()
|
||||
{
|
||||
Type type = Type.GetType("MyTempScript", true);
|
||||
|
||||
object instance = Activator.CreateInstance(type);
|
||||
|
||||
var verifyMethod = type.GetMethod("Verify", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
var verifyResult = verifyMethod.Invoke(instance, new object[0]);
|
||||
return verifyResult as string;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Documentation for RecompileScripts.](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-recompile-scripts.html)
|
||||
[Documentation for WaitForDomainReload.](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-wait-for-domain-reload.html)
|
@@ -0,0 +1,54 @@
|
||||
# 10\. Long running tests
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This exercise will cover best practices and pitfalls for tests that have a long runtime, such as tests yielding back a `WaitForSeconds`.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
In Play Mode it is possible for UnityTests to return [Yield instructions](https://docs.unity3d.com/ScriptReference/YieldInstruction.html), such as `WaitForSeconds`. This is supported because in some test cases it can be valid to wait for a limited time. However, long-running tests are in general a bad practice that should be avoided when possible. If you can't avoid a long-running test, it's recommended to provide the test with `[Category]` and `[Explicit]` attributes. The `[Category]` attribute is used to label tests with a category name that can later be used as a filter to run a subset of tests selectively. The `[Explicit]` attribute ensures that the test is not run by default when running all tests. The test is only run when it is explicitly selected in the UI, or when its category is selected.
|
||||
|
||||
```
|
||||
[UnityTest]
|
||||
[Explicit, Category("integration")]
|
||||
public IEnumerator MySlowTest()
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
In practice, this means that if you give some long-running tests the category "integration", then they will only be run if the "integration" category is selected. This makes it possible to keep "All tests" running relatively fast, even on a large project. It is also possible to specify the `[Explicit]` and `[Category]` attributes on a class level, which then applies to all tests in the class and on an assembly level, which applied to all tests inside that assembly. An example with it applied to assemblies:
|
||||
|
||||
```
|
||||
[assembly:Explicit]
|
||||
[assembly:Category("integration")]
|
||||
```
|
||||
|
||||
It is a good practice to have assembly level attributes defined in an `AssemblyInfo.cs` file.
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `10_LongRunningTests`, which is set up with a test assembly for Play Mode.
|
||||
|
||||
The exercise is to add a new `UnityTest`, which yields back a `WaitForSeconds` command and then tag it accordingly with `[Category]` and `[Explicit]` tags.
|
||||
|
||||
When pressing **RunAll**, the test should be skipped. When the Category is selected in the category drop down in the UI, then the test should not be skipped when **RunAll** is selected.
|
||||
|
||||
## Solution
|
||||
|
||||
The sample `10_LongRunningTests_Solution` contains the solution.
|
||||
|
||||
The implemented test can look like this:
|
||||
|
||||
```
|
||||
[UnityTest]
|
||||
[Explicit, Category("integration")]
|
||||
public IEnumerator ASlowTest()
|
||||
{
|
||||
yield return new WaitForSeconds(5);
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Nunit documentation for the Category attribute](https://docs.nunit.org/articles/nunit/writing-tests/attributes/category.html)
|
@@ -0,0 +1,9 @@
|
||||
# Unity Test Framework learning materials
|
||||
|
||||
This section contains courses to help you learn Unity Test Framework through a series of applied exercises. The courses are:
|
||||
|
||||
* The [Unity Test Framework General Introduction](./welcome.md), which gives you an opportunity to try out some of the framework's core features through general examples. Each exercise in this course is accompanied by a sample project and corresponding solution, which you can import from the Package Manager window.
|
||||
|
||||
* [Testing Lost Crypt](./LostCrypt/welcome.md), which shows you how to use the framework to test an actual game project.
|
||||
|
||||
We strongly recommend that you complete the General Introduction before you attempt Testing Lost Crypt.
|
@@ -0,0 +1,19 @@
|
||||
# 8\. PlayMode tests in a Player
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will teach you how to run Play Mode tests in a Standalone player on your machine.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
A huge area in our quality assurance using automated testing is the ability to test on different types of platforms. This is both useful for us and our customers, allowing them to verify their application on everything from phones to consoles.
|
||||
|
||||
The simplest setup to run on a platform is to run as standalone on your own computer. If you have the Unity standalone platform support for your OS installed, then you can run your Play Mode tests by clicking the **Run all in player** button. For more detailed instructions, see [Run play mode tests in a standalone player](../workflow-run-playmode-test-standalone.md).
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `8_PlayModeTests_InPlayer`.
|
||||
|
||||
This project contains the solution to the previous exercise, which is just a simple Play Mode test.
|
||||
|
||||
Execute the test in your standalone player by clicking the **Run all in player** button in the Play Mode tab.
|
@@ -0,0 +1,33 @@
|
||||
# 7\. PlayMode tests
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This exercise introduces the concept of Play Mode tests and will teach you:
|
||||
|
||||
* When to use Play Mode tests.
|
||||
* How to set up an assembly definition for PlayMode tests.
|
||||
* How to run tests in Play Mode.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
Managed code in Unity generally exists in two different modes: Edit Mode and Play Mode. Edit Mode is code executing inside the Editor, which includes things like our UIs and underlying logic. Play Mode is when the game or 3D application is playing, which is either when the user presses the play button in the Editor or when the code runs in a standalone player.
|
||||
|
||||
We have distinct tests for each mode because how they run and what they can access is different in each case. Methods from a given API may only be available in one mode. Due to this distinction, tests for Edit Mode and Play Mode are in different assemblies.
|
||||
|
||||
You can create a Play Mode test assembly by following the instructions in the Play Mode tab of the Test Runner UI. Detailed instructions are available in the [Getting started section](../workflow-create-test.md). The difference in the assembly definition between Edit Mode and Play Mode is what platforms they are enabled for. An Edit Mode test assembly is only enabled for the `Editor` platform. Enabling any other platforms automatically makes it a Play Mode test assembly, as tests can now run on other platforms. By default, Play Mode tests are set to run on all platforms.
|
||||
|
||||
## Exercise
|
||||
|
||||
The [sample](./welcome.md#import-samples) `7_PlayModeTests` contains an empty project. Import this sample and add a new assembly for Play Mode tests.
|
||||
|
||||
Afterwards add a test which just asserts that `Application.isPlaying` is true. This flag will only be true when in Play Mode.
|
||||
|
||||
Run the test. Notice that your Editor enters Play Mode (the equivalent of pressing the play button) while the test is running and exits Play Mode afterward.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution with the test and assembly setup is available in the `7_PlayModeTests_Solution` sample.
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[EditMode vs PlayMode](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/edit-mode-vs-play-mode-tests.html)
|
@@ -0,0 +1,25 @@
|
||||
# 14\. Preserve test state
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will cover how to let variables and information in your tests survive domain reloads using serialization.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
When a domain reload happens, all scripts are reloaded. That also means that most data in members of the test class are lost. In some cases that is an issue, as you might want to retain some information during a domain reload.
|
||||
|
||||
The solution to that is serialization. If you add a `[SerializeField]` attribute to the field in question, then it will retain its value. Note that there are some limitations to serialization in Unity, see [Unity Serialization](https://docs.unity3d.com/Manual/script-Serialization.html).
|
||||
|
||||
## Exercise
|
||||
|
||||
The [sample](./welcome.md#import-samples) `14_PreserveTestState` contains the solution for the previous assignment, with one exception; the file name is now a guid.
|
||||
|
||||
This means that in order to clean up correctly, the `TearDown` method needs to know the filename.
|
||||
|
||||
Currently, when running the test for the first time, the `TearDown` will fail because it's not given a file name. On subsequent runs of the test, it will fail due to duplicate files with the same C# script in it.
|
||||
|
||||
The task is to fix this loss of the file name info by using serialization.
|
||||
|
||||
## Solution
|
||||
|
||||
The solution is simple. Just add a `[SerializeField]` attribute to the filename field. The solution is included as sample `14_PreserveTestState_Solution.`
|
@@ -0,0 +1,38 @@
|
||||
# 1\. Running a test in a Unity project
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This exercise will teach you how to set up a simple Unity project with a test assembly and tests. It will also introduce the structure of unit tests based on NUnit.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
At Unity, our main way of testing content is using the Unity Test Framework, which comes as a default package in the Unity Editor. Knowing how to set up a basic project with tests can help you get started on your journey.
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `1_RunningTest_Project` into your Unity Editor (version 2019.2 or newer) from the Package Manager window.
|
||||
|
||||
**Note:** The project contains one `.cs` file (`MyMath.cs`), which is a simple math implementation. The exercise is to create unit tests for this class.
|
||||
|
||||
Open up the Test Runner UI (**Window > General > TestRunner**) and set up a new EditMode test assembly alongside the MyExercise folder. Detailed instructions are available in the [Getting started section](../workflow-create-test-assembly.md). Create a new test inside the new test assembly folder (default name is `Tests`) either from the Test Runner UI or by right-clicking in the Project window and selecting **Create > Testing > C# test script**. Before we do the test, it is also necessary to link up our test assembly with the existing code assembly. Click on the test assembly you created in the Project window to see it in the **Inspector** (Click on the Tests folder > Tests).
|
||||
|
||||
In the Assembly Definition References, you will see that `UnityEngine.TestRunner` and `UnityEditor.TestRunner` are already referenced, along with an assembly reference to NUnit. Click the \`+\` button in the Assembly Definition Reference part to add a new reference. Click on the little circle and select `MyExercise` and click **Apply** in the bottom of the inspector (you might need to scroll down).
|
||||
|
||||
Open up the C# solution with your IDE (Visual Studio or Rider) and open up the test file you created. You can delete the method with the `[UnityTest]` attribute, as you won't be needing that. In the method with `[Test]` attribute, you can add an assert statement, to verify that `MyMath.Add` works correctly. E.g. using `Assert.AreEqual`. Rename the method to be something more descriptive. A good practice is that the method name should describe what is being tested. For example, the class name could be `MyMathTests` and the first test could then be `AddsTwoPositiveIntegers`. If you want to, you can add additional methods that test other number combinations. It is a best practice that each test should have just one check.
|
||||
|
||||
Switch back to Unity and go to the Test Runner UI. Here you should now see a tree structure which includes your test assembly name, the class name and method name. This reflects the general structure of tests with NUnit, which is the framework that Unity Test Framework is built on top of. Each class can have multiple tests and there can be multiple test classes in a namespace / assembly. You can double click on your test name or any of its parents to run the test. You will see a green checkmark if your test code passes and a red cross if your test code failed. Note that if you do not see any tests, remember to check your console log. Any compile error would block all tests from being shown.
|
||||
|
||||
You can now go back to your test code and add tests for the `Subtract` method. Note that you will likely see the tests fail, as there is a bug in our `Subtract` method. After you have seen your test fail with a meaningful error (e.g. `Expected 2, but got 6`), you can go to `MyMath.cs` and fix the return value to be just `return a - b;`. Then rerun the test to verify that you fixed the error.
|
||||
|
||||
## Hints
|
||||
|
||||
* Sometimes the UI for creating a test assembly and creating your first test file can be a bit hard to use. If the Test Runner UI does not register your assembly, try clicking on the folder in the project window or navigate to the folder with the asmdef.
|
||||
|
||||
## Solution
|
||||
|
||||
A solution for this exercise can be found in the sample `1_RunningTest_Project_Solution`. The solution contains a `Tests` folder with an `asmdef` file and and one `.cs` file, containing the tests.
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
Read more about [Assembly Definitions in the Unity manual.](https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html)
|
||||
You can read more about unit tests in general at [Introduction To Unity Unit Testing](https://www.raywenderlich.com/9454-introduction-to-unity-unit-testing).
|
@@ -0,0 +1,33 @@
|
||||
# 17\. Running tests programmatically
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will introduce the TestRunnerApi, teaching you how to trigger a test run programmatically.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
A recent new feature in the test framework is the addition of the `TestRunnerApi`. This api allows for interactions with the test framework programmatically, such as listing tests, running tests and receiving test results.
|
||||
|
||||
For details and examples, see the TestRunnerApi documentation.
|
||||
|
||||
## Exercise
|
||||
|
||||
The [sample](./welcome.md#import-samples) `17_RunningTestsProgrammatically` contains a mono behavior script called `MyMonoBehaviour`, which has a property for whether it has been configured. The project also contains a scene with multiple game objects with `MyMonoBehaviour` on them.
|
||||
|
||||
The task is to create a set of scene validation tests, which verifies that the scene MyScene.unity:
|
||||
|
||||
* The scene contains precisely 5 game objects with `MyMonoBehaviour` on them.
|
||||
|
||||
* All game objects with `MyMonoBehaviour` must have `IsConfigured` set to true
|
||||
|
||||
After these tests have been created, implement a MenuItem, which can trigger the test run of the scene validation tests, using the `TestRunnerApi` and report the result to the console log.
|
||||
|
||||
It is recommended to give your scene validation test a category, so it is easier to make a filter that runs those exclusively.
|
||||
|
||||
## Hints
|
||||
|
||||
* Remember to include the test mode in the filter provided to `Execute`
|
||||
|
||||
## Solution
|
||||
|
||||
A full example solution for the excersise is available in the sample `17_RunningTestsProgrammatically_Solution`.
|
@@ -0,0 +1,61 @@
|
||||
# 11\. Scene-based tests
|
||||
|
||||
## Learning objectives
|
||||
|
||||
In this exercise, you will learn how to test content that is stored in a scene.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
A useful scenario for our customers is using the test framework for verifying the content of a scene. That could be checking for certain GameObjects and MonoBehaviors.
|
||||
|
||||
The [EditorSceneManager](https://docs.unity3d.com/ScriptReference/SceneManagement.EditorSceneManager.html) allows for loading and saving scenes. In combination with the test framework, this allows for the implementation of tests that verify a scene.
|
||||
|
||||
When changing the state of the Editor in a test, such as loading a scene, it's good practice to clean up afterward. This can be done in a method with the `[TearDown]` attribute.
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `11_SceneBasedTests`, which contains a scene named `MyGameScene` and an assembly for Edit Mode tests.
|
||||
|
||||
The task is to create a test that opens the scene, verifies that the scene contains a game object named `GameObjectToTestFor`.
|
||||
|
||||
As cleanup, it should open a new empty scene, which is the default for Edit Mode tests. It is recommended to put that in a `[TearDown]`, which ensures that the cleanup code is run, even if the test fails.
|
||||
|
||||
## Hints
|
||||
|
||||
* `EditorSceneManager.OpenScene("Assets\\MyGameScene.unity");` loads the scene
|
||||
* `EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);` cleans up by changing back to an empty scene.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution is available in the sample `11_SceneBasedTests_Solution`.
|
||||
|
||||
The test implementation can look like this:
|
||||
|
||||
```
|
||||
public class SceneTests
|
||||
{
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
EditorSceneManager.OpenScene("Assets\\MyGameScene.unity");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyScene()
|
||||
{
|
||||
var gameObject = GameObject.Find("GameObjectToTestFor");
|
||||
|
||||
Assert.That(gameObject, Is.Not.Null);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Documentation for EditorSceneManage api](https://docs.unity3d.com/ScriptReference/SceneManagement.EditorSceneManager.html)
|
@@ -0,0 +1,77 @@
|
||||
# 3\. Semantic test assertion
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This exercise introduces the `Assert.That` and related classes.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
The NUnit test framework and the Unity Test Framework have a series of classes for asserting objects in a way that is closer to natural language. This makes the statements easily readable.
|
||||
|
||||
Here are some examples on how to use the semantic assertion classes:
|
||||
|
||||
```
|
||||
Assert.That(myValue, Is.GreaterThan(20));
|
||||
Assert.That(str, Does.Contain("a string").And.Contain("something else"));
|
||||
```
|
||||
|
||||
Here we check that the variable `myValue` is greater than 20 and then that the string `str` contains both "a string" and "something else".
|
||||
|
||||
The semantic assertion is also known as [Constraint Model](https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertion-models/constraint.html). Other than `It` and `Does` there are multiple other keywords that can be used.
|
||||
|
||||
## Exercise
|
||||
|
||||
In the `3_SemanticTestAssertion` [sample](./welcome.md#import-samples), there is a class called `ValueOutputter`, which returns values of different types.
|
||||
|
||||
Write tests that assert on the different outputs. It should be verified that:
|
||||
|
||||
* `GetInt()` returns 11.
|
||||
* `GetString()` returns a string that contains the words `string` and `asserted`.
|
||||
* `GetFloat()` returns a value that is around 19.33.
|
||||
|
||||
## Hints
|
||||
|
||||
* Asserting on the float might require a check for the value being greater than 19.33 and less than 19.34, as the output is not rational.
|
||||
|
||||
## Solution
|
||||
|
||||
A full solution to the exercise is available in the sample `3_SemanticTestAssertion_Solution`.
|
||||
|
||||
```
|
||||
internal class ValueOutputterTests
|
||||
{
|
||||
[Test]
|
||||
public void GivesExpectedInt()
|
||||
{
|
||||
var outputterUnderTest = new ValueOutputter();
|
||||
|
||||
var number = outputterUnderTest.GetInt();
|
||||
|
||||
Assert.That(number, Is.EqualTo(11));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GivesExpectedString()
|
||||
{
|
||||
var outputterUnderTest = new ValueOutputter();
|
||||
|
||||
var str = outputterUnderTest.GetString();
|
||||
|
||||
Assert.That(str, Does.Contain("string").And.Contain("asserted"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GivesExpectedFloat()
|
||||
{
|
||||
var outputterUnderTest = new ValueOutputter();
|
||||
|
||||
var number = outputterUnderTest.GetFloat();
|
||||
|
||||
Assert.That(number, Is.GreaterThan(19.33f).And.LessThan(19.34f));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[NUnit 2 documentation for the constraint model](https://nunit.org/docs/2.4/constraintModel.html)
|
@@ -0,0 +1,102 @@
|
||||
# 6\. SetUp and TearDown
|
||||
|
||||
## Learning objectives
|
||||
|
||||
In this exercise, you will get practical experience in using the NUnit attributes `[SetUp]` and `[TearDown]` in order to reduce code duplication in your tests.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
It's good practice to always let your test code clean up after itself and you also often need to set things up before running a test. If you have multiple tests, then that can easily become a lot of code duplication and if your test fails, your cleanup might not even be run, if you have not wrapped it in `try` and `finally` blocks.
|
||||
|
||||
As a solution to this, NUnit has the `[SetUp]` and `[TearDown]` attributes. Methods with this attribute will be run before and after any of the classes respectively. If you are running multiple tests in your class at once, then the teardown and setup are run in between each of the tests.
|
||||
|
||||
```
|
||||
public class TestClass
|
||||
{
|
||||
[SetUp]
|
||||
public void MySetUp() { ... }
|
||||
|
||||
[Test]
|
||||
public void MyFirstTest() { ... }
|
||||
|
||||
[Test]
|
||||
public void MySecondTest() { ... }
|
||||
|
||||
[TearDown]
|
||||
public void MyTearDown() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
## Exercise
|
||||
|
||||
Import the [sample](./welcome.md#import-samples) `6_SetUpTearDown`.
|
||||
|
||||
In this project there is a class called `FileCreator`. It has two methods:
|
||||
|
||||
* `CreateEmptyFile(fileName)` - Creates an empty file in an `OutputFiles` directory
|
||||
|
||||
* `CreateFile(string fileName, string content)` - Creates a file with the given content in an `OutputFiles` directory
|
||||
|
||||
The catch is that it will throw a `DirectoryNotFoundException`, if there is no output called `OutputFiles` in the current directory. You will need to create this directory inside a `SetUp` method and remove it again afterwards with a `TearDown`. Your test can then assume that it starts with an emtpy directory, which simplifies the assertion.
|
||||
|
||||
## Hints
|
||||
|
||||
* You can use `Directory.CreateDirectory` to create a directory.
|
||||
* You can use `Directory.Delete` with the recursive flag (second argument) set to delete the directory along with all its files.
|
||||
* `Directory.GetFiles` can be used to get files in a given directory.
|
||||
* `Path.Combine` is a handy method for combining parts of a file path. For example the directory name and the file name.
|
||||
|
||||
## Solution
|
||||
|
||||
The exercise can be solved with a test like the following:
|
||||
|
||||
```
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Directory.CreateDirectory(FileCreator.k_Directory);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreatesEmptyFile()
|
||||
{
|
||||
var fileCreatorUnderTest = new FileCreator();
|
||||
var expectedFileName = "MyEmptyFile.txt";
|
||||
|
||||
fileCreatorUnderTest.CreateEmptyFile(expectedFileName);
|
||||
|
||||
var files = Directory.GetFiles(FileCreator.k_Directory);
|
||||
Assert.That(files.Length, Is.EqualTo(1), "Expected one file.");
|
||||
var expectedFilePath = Path.Combine(FileCreator.k_Directory, expectedFileName);
|
||||
Assert.That(files[0], Is.EqualTo(expectedFilePath));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreatesFile()
|
||||
{
|
||||
var fileCreatorUnderTest = new FileCreator();
|
||||
var expectedFileName = "MyFile.txt";
|
||||
var expectedContent = "TheFileContent";
|
||||
|
||||
fileCreatorUnderTest.CreateFile(expectedFileName, expectedContent);
|
||||
|
||||
var files = Directory.GetFiles(FileCreator.k_Directory);
|
||||
Assert.That(files.Length, Is.EqualTo(1), "Expected one file.");
|
||||
var expectedFilePath = Path.Combine(FileCreator.k_Directory, expectedFileName);
|
||||
Assert.That(files[0], Is.EqualTo(expectedFilePath));
|
||||
var content = File.ReadAllText(expectedFilePath);
|
||||
Assert.That(content, Is.EqualTo(expectedContent));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
Directory.Delete(FileCreator.k_Directory, true);
|
||||
}
|
||||
```
|
||||
|
||||
A full project with the solution can be found in the sample `6_SetUpTearDown.`
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[Nunit documentation for SetUp and TearDown](https://docs.nunit.org/articles/nunit/technical-notes/usage/SetUp-and-TearDown.html)
|
@@ -0,0 +1,140 @@
|
||||
# 15\. Test cases
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will cover `[TestCase]` and similar NUnit attributes and how to work with them in UnityTests.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
NUnit has a few tools for parameterized tests, which can be used to specify test cases with variating parameters. This can drastically reduce the amount of repeated code and make the test cleaner to use.
|
||||
|
||||
An example of a parameterized test using the `[TestCase]` attribute:
|
||||
|
||||
```
|
||||
[Test]
|
||||
[TestCase(49, "a string", true)]
|
||||
[TestCase(9, "something", false)]
|
||||
public void MyTest(int firstValue, string secondValue, bool expectedOutcome)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This will generate two tests, each with a different input to the method body.
|
||||
|
||||
In addition to the `[TestCase]` attribute, NUnit also has a `[Values]` attribute, which specifies a set of values on each individual input. An example of such is:
|
||||
|
||||
```
|
||||
[Test]
|
||||
public void MyTest([Values(49, 9)]int firstValue, [Values("a string", "something")]string secondValue)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
When specifying multiple input parameters, they are treated as combinatorial. That means that each combination of them will be tested. For the above example, that will result in a total of 4 cases:
|
||||
|
||||
```
|
||||
MyTest(49, "a string")
|
||||
MyTest(49, "something")
|
||||
MyTest(9, "a string")
|
||||
MyTest(9, "something")
|
||||
```
|
||||
|
||||
This can easily explode into many combinations. The combinations might not all be valuable and would just waste time, so use this with care.
|
||||
|
||||
For both the `[TestCase]` and `[Values]` attributes, there is a more dynamic version called `[TestCaseSource]` and `[ValueSource]` accordingly. These each take in a static method or array, returning a collection of objects.
|
||||
|
||||
Of these 4 methods, the `[ValueSource]` attribute is currently the only one supported by `[UnityTest]`. Since this would produce combinational tests, if multiple arguments with `[ValueSource]` are provided, then it is recommended to make a test case struct, if multiple arguments are needed for the test. An example of such could look like this:
|
||||
|
||||
```
|
||||
[UnityTest]
|
||||
public IEnumerator AddAsyncCalculatesCorrectValue([ValueSource(nameof(TestCases))] TestCase testCase)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
private static IEnumerable TestCases()
|
||||
{
|
||||
yield return new TestCase {value1 = 4, value2 = "a string"};
|
||||
yield return new TestCase {value1 = 8, value2 = "another string"};
|
||||
}
|
||||
|
||||
public struct TestCase
|
||||
{
|
||||
public int value1;
|
||||
public string value2;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{value1}, {value2}";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exercise
|
||||
|
||||
In the [sample](./welcome.md#import-samples) `15_TestCases` a class is set up with some basic math. It has two methods:
|
||||
|
||||
* `Add` which takes two integers and adds them together.
|
||||
|
||||
* `AddAsync` also adds two integers together, but does so asynchronously, yielding back an `IEnumerator`
|
||||
|
||||
The task is to add tests for the two methods. The `AddAsync` method first returns the result after a few frames, so that will be best suited for a `[UnityTest]`. Note that it is not enough to yield back the `IEnumerator`, as the test framework does not curently support nested yields. Instead, create a loop to move over each element until it's done. At each step of the while loop, let the test yield back null.
|
||||
|
||||
## Hints
|
||||
|
||||
* The `ToString()` implementation in the struct is there to provide readable info in the test runner treeview. Without it, it would just show the struct name as the test argument for every case.
|
||||
|
||||
## Solution
|
||||
|
||||
A solution for the exercise is available in the sample `15_TestCases_Solution`. Tests for both methods can be implemented as follows:
|
||||
|
||||
```
|
||||
[Test]
|
||||
[TestCase(24, 80, 104)]
|
||||
[TestCase(10, -15, -5)]
|
||||
[TestCase(int.MaxValue, 10, int.MinValue + 9)]
|
||||
public void AddCalculatesCorrectValue(int valueA, int valueB, int expectedResult)
|
||||
{
|
||||
var myClass = new MyClass();
|
||||
|
||||
var result = myClass.Add(valueA, valueB);
|
||||
|
||||
Assert.That(result, Is.EqualTo(expectedResult));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AddAsyncCalculatesCorrectValue([ValueSource(nameof(AdditionCases))] AddCase addCase)
|
||||
{
|
||||
var myClass = new MyClass();
|
||||
|
||||
var enumerator = myClass.AddAsync(addCase.valueA, addCase.valueB);
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
var result = enumerator.Current;
|
||||
|
||||
Assert.That(result, Is.EqualTo(addCase.expectedResult));
|
||||
}
|
||||
|
||||
private static IEnumerable AdditionCases()
|
||||
{
|
||||
yield return new AddCase {valueA = 24, valueB = 80, expectedResult = 104};
|
||||
yield return new AddCase {valueA = 10, valueB = -15, expectedResult = -5};
|
||||
yield return new AddCase {valueA = int.MaxValue, valueB = 10, expectedResult = int.MinValue + 9};
|
||||
}
|
||||
|
||||
public struct AddCase
|
||||
{
|
||||
public int valueA;
|
||||
public int valueB;
|
||||
public int expectedResult;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{valueA} + {valueB} = {expectedResult}";
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,51 @@
|
||||
# 9\. Using the UnityTest Attribute
|
||||
|
||||
## Learning objectives
|
||||
|
||||
This section will introduce you to the custom `[UnityTest]` Attribute, which allows for creating tests that run over multiple frames.
|
||||
|
||||
## Intro and motivation
|
||||
|
||||
An important extension to the Nunit framework that we've made is introducing the `[UnityTest]` attribute. The attribute allows for creating tests that can yield and resume running after a certain condition. Therefore the test must have the return type of `IEnumerator`. You can then yield back a yield instruction or null, like so:
|
||||
|
||||
```
|
||||
[UnityTest]
|
||||
public IEnumerator MyTest()
|
||||
{
|
||||
DoSomething();
|
||||
// Skip 1 frame.
|
||||
yield return null;
|
||||
DoSomethingElse();
|
||||
}
|
||||
```
|
||||
|
||||
In the snippet above we call the `DoSomething` method, then skip one frame before calling the `DoSomethingElse` method.
|
||||
|
||||
For more information on the yield keyword in C#, see the [Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield).
|
||||
|
||||
## Exercise
|
||||
|
||||
In the [sample](./welcome.md#import-samples) `9_UnityTestAttribute` you will find a Play Mode test assembly set up with one Play Mode test in it. The PlayMode test does not have a body yet, but there is a function called `PrepareCube()` which will set up a cube with some physics applied.
|
||||
|
||||
The task is to initialize the cube and then verify that it has moved after one frame has passed.
|
||||
|
||||
## Solution
|
||||
|
||||
The full solution is available in the `9_UnityTestAttribute_Solution` sample.
|
||||
|
||||
```
|
||||
[UnityTest]
|
||||
public IEnumerator CubeMovesDown()
|
||||
{
|
||||
var cubeUnderTest = PrepareCube();
|
||||
var initialPosition = cubeUnderTest.transform.position;
|
||||
|
||||
yield return null;
|
||||
|
||||
Assert.That(cubeUnderTest.transform.position, Is.Not.EqualTo(initialPosition));
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading and resources
|
||||
|
||||
[UTF documentation regarding UnityTest attribute](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-attribute-unitytest.html)
|
@@ -0,0 +1,43 @@
|
||||
# Welcome
|
||||
|
||||
Welcome to the Unity Test Framework general introduction course.
|
||||
|
||||
This course consists of different exercises to help you learn fundamental Unity Test Framework concepts through practical examples. Each exercise has a **Learning Objectives** section to help you identify the skills you will learn. The exercises are grouped thematically, and their difficulty varies.
|
||||
|
||||
After completing an exercise, you can check your solution against the one provided. Note that many of the exercises can be solved in several possible ways.
|
||||
|
||||
## Import samples
|
||||
|
||||
Project files for each exercise and its accompanying solution are provided as samples with the Unity Test Framework package. To import an exercise or solution to your Unity Editor:
|
||||
|
||||
1. Go to **Window > Package Manager** and, in the [packages list view](https://docs.unity3d.com/Manual/upm-ui-list.html), selct Unity Test Framework.
|
||||
2. In the package [details view](https://docs.unity3d.com/Manual/upm-ui-details.html), find the **Samples** section.
|
||||
3. Find the exercise or solution you want to import and click the import button.
|
||||
|
||||

|
||||
|
||||
> Note: You can import an exercise and its solution or multiple exercises at the same time, but since several of the exercises use the same naming pattern this will likely result in compilation errors that prevent you running tests or building your project. The recommended workflow is to import and work on one exercise at a time. If you import additional exercises or solutions for reference, you can delete them again before running your main exercise.
|
||||
|
||||
## Course outline
|
||||
|
||||
1. [Running a test in a Unity project](./running-test.md)
|
||||
2. [Arrange, act, assert](./arrange-act-assert.md)
|
||||
3. [Semantic test assertion](./semantic-test-assertion.md)
|
||||
4. [Custom comparison](./custom-comparison.md)
|
||||
5. [Asserting logs](./asserting-logs.md)
|
||||
6. [Setup and teardown](./setup-teardown.md)
|
||||
7. [Play mode tests](./play-mode-tests.md)
|
||||
8. [Play mode tests in a player](./play-mode-tests-in-player.md)
|
||||
9. [Using the UnityTest attribute](./unitytest-attribute.md)
|
||||
10. [Long-running tests](./long-running-tests.md)
|
||||
11. [Scene-based tests](./scene-based-tests.md)
|
||||
12. [Setup and cleanup at build time](./build-setup-cleanup.md)
|
||||
13. [Domain reload](./domain-reload.md)
|
||||
14. [Preserve test state](./preserve-test-state.md)
|
||||
15. [Test cases](./test-cases.md)
|
||||
16. [Custom attributes](./custom-attributes.md)
|
||||
17. [Running tests programmatically](./running-tests-programmatically.md)
|
||||
|
||||
## Introduction to the Unity Test Framework (UTF)
|
||||
|
||||
Here you can find a [video introduction](https://www.youtube.com/watch?v=wTiF2D0_vKA) to the Unity Test Framework.
|
@@ -0,0 +1,53 @@
|
||||
# Edit Mode vs. Play Mode tests
|
||||
|
||||
Let’s clarify a bit what Play Mode and Edit Mode test means from the Unity Test Framework perspective:
|
||||
|
||||
## Edit Mode tests
|
||||
|
||||
**Edit Mode** tests (also known as Editor tests) are only run in the Unity Editor and have access to the Editor code in addition to the game code.
|
||||
|
||||
With Edit Mode tests it is possible to test any of your [Editor extensions](https://docs.unity3d.com/Manual/ExtendingTheEditor.html) using the [UnityTest](./reference-attribute-unitytest.md) attribute. For Edit Mode tests, your test code runs in the [EditorApplication.update](https://docs.unity3d.com/ScriptReference/EditorApplication-update.html) callback loop.
|
||||
|
||||
> **Note**: You can also control entering and exiting Play Mode from your Edit Mode test. This allow your test to make changes before entering Play Mode.
|
||||
|
||||
Edit Mode tests should meet one of the following conditions:
|
||||
|
||||
* They should have an [assembly definition](./workflow-create-test-assembly.md) with reference to *nunit.framework.dll* and has only the Editor as a target platform:
|
||||
|
||||
```assembly
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
```
|
||||
|
||||
* Legacy condition: put tests in the project’s [Editor](https://docs.unity3d.com/Manual/SpecialFolders.html) folder.
|
||||
|
||||
## Play Mode tests
|
||||
|
||||
You can run **Play Mode** tests as a [standalone in a Player](./workflow-run-playmode-test-standalone.md) or inside the Editor. Play Mode tests allow you to exercise your game code, as the tests run as [coroutines](https://docs.unity3d.com/ScriptReference/Coroutine.html) if marked with the `UnityTest` attribute.
|
||||
|
||||
Play Mode tests should correspond to the following conditions:
|
||||
|
||||
* Have an [assembly definition](./workflow-create-test-assembly.md) with reference to *nunit.framework.dll*.
|
||||
* Have the test scripts located in a folder with the .asmdef file.
|
||||
* The test assembly should reference an assembly within the code that you need to test.
|
||||
|
||||
```assembly
|
||||
"references": [
|
||||
"NewAssembly"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
```
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Attributes
|
||||
|
||||
Use the [NUnit](http://www.nunit.org/) `Test` attribute instead of the `UnityTest` attribute, unless you need to [yield special instructions](./reference-custom-yield-instructions.md), in Edit Mode, or if you need to skip a frame or wait for a certain amount of time in Play Mode.
|
||||
|
||||
### References
|
||||
|
||||
It is possible for your Test Assemblies to reference the test tools in `UnityEngine.TestRunner` and `UnityEditor.TestRunner`. The latter is only available in Edit Mode. You can specify these references in the `Assembly Definition References` on the Assembly Definition.
|
@@ -0,0 +1,10 @@
|
||||
# Extending Unity Test Framework
|
||||
It is possible to extend the Unity Test Framework (UTF) in many ways, for custom workflows for your projects and for other packages to build on top of UTF.
|
||||
|
||||
These extensions are a supplement to the ones already offered by [NUnit](https://github.com/nunit/docs/wiki/Framework-Extensibility).
|
||||
|
||||
Some workflows for extending UTF include:
|
||||
* [How to split the build and run process for standalone Play Mode tests](./reference-attribute-testplayerbuildmodifier.md#split-build-and-run-for-player-mode-tests)
|
||||
* [How to run tests programmatically](./extension-run-tests.md)
|
||||
* [How to get test results](./extension-get-test-results.md)
|
||||
* [How to retrieve the list of tests](./extension-retrieve-test-list.md)
|
@@ -0,0 +1,45 @@
|
||||
# How to get test results
|
||||
You can receive callbacks when the active test run, or individual tests, starts and finishes. You can register callbacks by invoking `RegisterCallbacks` on the [TestRunnerApi](./reference-test-runner-api.md) with an instance of a class that implements [ICallbacks](./reference-icallbacks.md). There are four `ICallbacks` methods for the start and finish of both the whole run and each level of the test tree.
|
||||
|
||||
## Example
|
||||
An example of how listeners can be set up:
|
||||
|
||||
> **Note**: Listeners receive callbacks from all test runs, regardless of the registered `TestRunnerApi` for that instance.
|
||||
|
||||
``` C#
|
||||
public void SetupListeners()
|
||||
{
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
api.RegisterCallbacks(new MyCallbacks());
|
||||
}
|
||||
|
||||
private class MyCallbacks : ICallbacks
|
||||
{
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResultAdaptor result)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
if (!result.HasChildren && result.ResultState != "Passed")
|
||||
{
|
||||
Debug.Log(string.Format("Test {0} {1}", result.Test.Name, result.ResultState));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: The registered callbacks are not persisted on domain reloads. So it is necessary to re-register the callback after a domain reloads, usually with [InitializeOnLoad](https://docs.unity3d.com/Manual/RunningEditorCodeOnLaunch.html).
|
||||
|
||||
It is possible to provide a `priority` as an integer as the second argument when registering a callback. This influences the invocation order of different callbacks. The default value is zero. It is also possible to provide `RegisterCallbacks` with a class instance that implements [IErrorCallbacks](./reference-ierror-callbacks.md) that is an extended version of `ICallbacks`. `IErrorCallbacks` also has a callback method for `OnError` that invokes if the run fails to start, for example, due to compilation errors or if an [IPrebuildSetup](./reference-setup-and-cleanup.md) throws an exception.
|
@@ -0,0 +1,13 @@
|
||||
# How to retrieve the list of tests
|
||||
It is possible to use the [TestRunnerApi](./reference-test-runner-api.md) to retrieve the test tree for a given test mode (**Edit Mode** or **Play Mode**). You can retrieve the test tree by invoking `RetrieveTestList` with the desired `TestMode` and a callback action, with an [ITestAdaptor](./reference-itest-adaptor.md) representing the test tree.
|
||||
|
||||
## Example
|
||||
The following example retrieves the test tree for Edit Mode tests and prints the number of total test cases:
|
||||
``` C#
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
api.RetrieveTestList(TestMode.EditMode, (testRoot) =>
|
||||
{
|
||||
Debug.Log(string.Format("Tree contains {0} tests.", testRoot.TestCaseCount));
|
||||
});
|
||||
```
|
||||
|
@@ -0,0 +1,72 @@
|
||||
# How to run tests programmatically
|
||||
## Filters
|
||||
|
||||
Run tests by calling `Execute` on the [TestRunnerApi](./reference-test-runner-api.md), and provide some execution settings that consists of a [Filter](./reference-filter.md). The `Filter` specifies what tests to run.
|
||||
|
||||
### Example
|
||||
|
||||
The following is an example of how to run all **Play Mode** tests in a project:
|
||||
|
||||
``` C#
|
||||
var testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
var filter = new Filter()
|
||||
{
|
||||
testMode = TestMode.PlayMode
|
||||
};
|
||||
testRunnerApi.Execute(new ExecutionSettings(filter));
|
||||
```
|
||||
## Multiple filter values
|
||||
|
||||
It is possible to specify a more specific filter by filling out the fields on the `Filter` class in more detail.
|
||||
|
||||
Many of the fields allow for multiple values. The runner tries to match tests against at least one of the values provided and then runs any tests that match.
|
||||
|
||||
### Example
|
||||
|
||||
In this example, the API runs tests with full names that fit either of the two names provided:
|
||||
|
||||
``` C#
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
api.Execute(new ExecutionSettings(new Filter()
|
||||
{
|
||||
testNames = new[] {"MyTestClass.NameOfMyTest", "SpecificTestFixture.NameOfAnotherTest"}
|
||||
}));
|
||||
```
|
||||
## Multiple filter fields
|
||||
|
||||
If using multiple different fields on the filter, then it matches against tests that fulfill all the different fields.
|
||||
|
||||
### Example
|
||||
|
||||
In this example, it runs any test that fits either of the two test names, and that also belongs to a test assembly that fits the given name.
|
||||
|
||||
``` C#
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
api.Execute(new ExecutionSettings(new Filter()
|
||||
{
|
||||
assemblyNames = new [] {"MyTestAssembly"},
|
||||
testNames = new [] {"MyTestClass.NameOfMyTest", "MyTestClass.AnotherNameOfATest"}
|
||||
}));
|
||||
```
|
||||
## Multiple constructor filters
|
||||
|
||||
The execution settings take one or more filters in its constructor. If there is no filter provided, then it runs all **Edit Mode** tests by default. If there are multiple filters provided, then a test runs if it matches any of the filters.
|
||||
|
||||
### Example
|
||||
|
||||
In this example, it runs any tests that are either in the assembly named `MyTestAssembly` or if the full name of the test matches either of the two provided test names:
|
||||
|
||||
``` C#
|
||||
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
api.Execute(new ExecutionSettings(
|
||||
new Filter()
|
||||
{
|
||||
assemblyNames = new[] {"MyTestAssembly"},
|
||||
},
|
||||
new Filter()
|
||||
{
|
||||
testNames = new[] {"MyTestClass.NameOfMyTest", "MyTestClass.AnotherNameOfATest"}
|
||||
}
|
||||
));
|
||||
```
|
||||
> **Note**: Specifying different test modes or platforms in each `Filter` is not currently supported. The test mode and platform is from the first `Filter` only and defaults to Edit Mode, if not supplied.
|
@@ -0,0 +1,18 @@
|
||||
# Getting started with Unity Test Framework
|
||||
|
||||
To access the Unity Test Framework (UTF) in the Unity Editor, open the **Test Runner** window; go to **Window** > **General** > **Test Runner**.
|
||||
|
||||

|
||||
|
||||
To get started with UTF, follow the workflows below:
|
||||
|
||||
* [How to create a new test assembly](./workflow-create-test-assembly.md)
|
||||
* [How to create a test](./workflow-create-test.md)
|
||||
* [How to run a test](./workflow-run-test.md)
|
||||
* [How to create a Play Mode test](./workflow-create-playmode-test.md)
|
||||
* [How to run a Play Mode test as standalone](./workflow-run-playmode-test-standalone.md)
|
||||
|
||||
|
||||
|
||||
For further information, see the [resources](./resources.md) and [reference](./manual.md#reference) sections.
|
||||
|
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 414 KiB |
After Width: | Height: | Size: 240 KiB |
After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 51 KiB |