Initial Commit
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ModestTree;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using Zenject.Internal;
|
||||
using Assert = ModestTree.Assert;
|
||||
|
||||
// Ignore warning about using SceneManager.UnloadScene instead of SceneManager.UnloadSceneAsync
|
||||
#pragma warning disable 618
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public abstract class SceneTestFixture
|
||||
{
|
||||
readonly List<DiContainer> _sceneContainers = new List<DiContainer>();
|
||||
|
||||
bool _hasLoadedScene;
|
||||
DiContainer _sceneContainer;
|
||||
|
||||
protected DiContainer SceneContainer
|
||||
{
|
||||
get { return _sceneContainer; }
|
||||
}
|
||||
|
||||
protected IEnumerable<DiContainer> SceneContainers
|
||||
{
|
||||
get { return _sceneContainers; }
|
||||
}
|
||||
|
||||
public IEnumerator LoadScene(string sceneName)
|
||||
{
|
||||
return LoadScenes(sceneName);
|
||||
}
|
||||
|
||||
public IEnumerator LoadScenes(params string[] sceneNames)
|
||||
{
|
||||
Assert.That(!_hasLoadedScene, "Attempted to load scene twice!");
|
||||
_hasLoadedScene = true;
|
||||
|
||||
// Clean up any leftovers from previous test
|
||||
ZenjectTestUtil.DestroyEverythingExceptTestRunner(false);
|
||||
|
||||
Assert.That(SceneContainers.IsEmpty());
|
||||
|
||||
for (int i = 0; i < sceneNames.Length; i++)
|
||||
{
|
||||
var sceneName = sceneNames[i];
|
||||
|
||||
Assert.That(Application.CanStreamedLevelBeLoaded(sceneName),
|
||||
"Cannot load scene '{0}' for test '{1}'. The scenes used by SceneTestFixture derived classes must be added to the build settings for the test to work",
|
||||
sceneName, GetType());
|
||||
|
||||
Log.Info("Loading scene '{0}' for testing", sceneName);
|
||||
|
||||
var loader = SceneManager.LoadSceneAsync(sceneName, i == 0 ? LoadSceneMode.Single : LoadSceneMode.Additive);
|
||||
|
||||
while (!loader.isDone)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SceneContext sceneContext = null;
|
||||
|
||||
if (ProjectContext.HasInstance)
|
||||
// ProjectContext might be null if scene does not have a scene context
|
||||
{
|
||||
var scene = SceneManager.GetSceneByName(sceneName);
|
||||
|
||||
sceneContext = ProjectContext.Instance.Container.Resolve<SceneContextRegistry>()
|
||||
.TryGetSceneContextForScene(scene);
|
||||
}
|
||||
|
||||
_sceneContainers.Add(sceneContext == null ? null : sceneContext.Container);
|
||||
}
|
||||
|
||||
_sceneContainer = _sceneContainers.Where(x => x != null).LastOrDefault();
|
||||
|
||||
if (_sceneContainer != null)
|
||||
{
|
||||
_sceneContainer.Inject(this);
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
StaticContext.Clear();
|
||||
SetMemberDefaults();
|
||||
}
|
||||
|
||||
void SetMemberDefaults()
|
||||
{
|
||||
_hasLoadedScene = false;
|
||||
_sceneContainer = null;
|
||||
_sceneContainers.Clear();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public virtual void Teardown()
|
||||
{
|
||||
ZenjectTestUtil.DestroyEverythingExceptTestRunner(true);
|
||||
StaticContext.Clear();
|
||||
SetMemberDefaults();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 325fb0e9bd879b440a9b5c077309c199
|
||||
timeCreated: 1527233974
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public class SceneTestFixtureSceneReference : ScriptableObject
|
||||
{
|
||||
public SceneAsset Scene;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b634c456740a0e42928511e3f2ada75
|
||||
timeCreated: 1527233974
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class ValidateOnlyAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f9f4a0ef011f8846b17c8dc23e69b11
|
||||
timeCreated: 1506450521
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Zenject-TestFramework",
|
||||
"references": [
|
||||
"Zenject"
|
||||
],
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2f2239355369ba4fb6909aeaa41def5
|
||||
timeCreated: 1531969421
|
||||
licenseType: Free
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Zenject.Internal;
|
||||
using ModestTree;
|
||||
using Assert = ModestTree.Assert;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
public abstract class ZenjectIntegrationTestFixture
|
||||
{
|
||||
SceneContext _sceneContext;
|
||||
|
||||
bool _hasEndedInstall;
|
||||
bool _hasStartedInstall;
|
||||
|
||||
protected DiContainer Container
|
||||
{
|
||||
get
|
||||
{
|
||||
Assert.That(_hasStartedInstall,
|
||||
"Must call PreInstall() before accessing ZenjectIntegrationTestFixture.Container!");
|
||||
return _sceneContext.Container;
|
||||
}
|
||||
}
|
||||
|
||||
protected SceneContext SceneContext
|
||||
{
|
||||
get
|
||||
{
|
||||
Assert.That(_hasStartedInstall,
|
||||
"Must call PreInstall() before accessing ZenjectIntegrationTestFixture.SceneContext!");
|
||||
return _sceneContext;
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Assert.That(Application.isPlaying,
|
||||
"ZenjectIntegrationTestFixture is meant to be used for play mode tests only. Please ensure your test file '{0}' is outside of the editor folder and try again.", GetType());
|
||||
|
||||
ZenjectTestUtil.DestroyEverythingExceptTestRunner(true);
|
||||
StaticContext.Clear();
|
||||
}
|
||||
|
||||
protected void SkipInstall()
|
||||
{
|
||||
PreInstall();
|
||||
PostInstall();
|
||||
}
|
||||
|
||||
protected void PreInstall()
|
||||
{
|
||||
Assert.That(!_hasStartedInstall, "Called PreInstall twice in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
_hasStartedInstall = true;
|
||||
|
||||
Assert.That(!ProjectContext.HasInstance);
|
||||
|
||||
var shouldValidate = CurrentTestHasAttribute<ValidateOnlyAttribute>();
|
||||
ProjectContext.ValidateOnNextRun = shouldValidate;
|
||||
|
||||
Assert.That(_sceneContext == null);
|
||||
|
||||
_sceneContext = SceneContext.Create();
|
||||
_sceneContext.Install();
|
||||
|
||||
Assert.That(ProjectContext.HasInstance);
|
||||
|
||||
Assert.IsEqual(shouldValidate, ProjectContext.Instance.Container.IsValidating);
|
||||
Assert.IsEqual(shouldValidate, _sceneContext.Container.IsValidating);
|
||||
}
|
||||
|
||||
bool CurrentTestHasAttribute<T>()
|
||||
where T : Attribute
|
||||
{
|
||||
return GetType().GetMethod(TestContext.CurrentContext.Test.MethodName)
|
||||
.GetCustomAttributes(true)
|
||||
.Cast<Attribute>().OfType<T>().Any();
|
||||
}
|
||||
|
||||
protected void PostInstall()
|
||||
{
|
||||
Assert.That(_hasStartedInstall,
|
||||
"Called PostInstall but did not call PreInstall in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
|
||||
Assert.That(!_hasEndedInstall, "Called PostInstall twice in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
|
||||
_hasEndedInstall = true;
|
||||
_sceneContext.Resolve();
|
||||
|
||||
Container.Inject(this);
|
||||
|
||||
if (!Container.IsValidating)
|
||||
{
|
||||
// We don't have to do this here but it's kind of convenient
|
||||
// We could also remove it and just require that users add a yield after calling
|
||||
// and it would have the same effect
|
||||
Container.Resolve<MonoKernel>().Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerator DestroyEverything()
|
||||
{
|
||||
Assert.That(_hasStartedInstall,
|
||||
"Called DestroyAll but did not call PreInstall (or SkipInstall) in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
DestroyEverythingInternal(false);
|
||||
// Wait one frame for GC to really destroy everything
|
||||
yield return null;
|
||||
}
|
||||
|
||||
void DestroyEverythingInternal(bool immediate)
|
||||
{
|
||||
if (_sceneContext != null)
|
||||
{
|
||||
// We need to use DestroyImmediate so that all the IDisposable's etc get processed immediately before
|
||||
// next test runs
|
||||
if (immediate)
|
||||
{
|
||||
GameObject.DestroyImmediate(_sceneContext.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject.Destroy(_sceneContext.gameObject);
|
||||
}
|
||||
|
||||
_sceneContext = null;
|
||||
}
|
||||
|
||||
ZenjectTestUtil.DestroyEverythingExceptTestRunner(immediate);
|
||||
StaticContext.Clear();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
if (TestContext.CurrentContext.Result.Outcome == ResultState.Success)
|
||||
{
|
||||
Assert.That(_hasStartedInstall,
|
||||
"PreInstall (or SkipInstall) was not called in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
|
||||
Assert.That(_hasEndedInstall,
|
||||
"PostInstall was not called in test '{0}'!", TestContext.CurrentContext.Test.Name);
|
||||
}
|
||||
|
||||
DestroyEverythingInternal(true);
|
||||
|
||||
_hasStartedInstall = false;
|
||||
_hasEndedInstall = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0464fe2b382d1d4babf13bf0c3fa044
|
||||
timeCreated: 1506429461
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using ModestTree;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Zenject.Internal
|
||||
{
|
||||
public static class ZenjectTestUtil
|
||||
{
|
||||
public const string UnitTestRunnerGameObjectName = "Code-based tests runner";
|
||||
|
||||
public static void DestroyEverythingExceptTestRunner(bool immediate)
|
||||
{
|
||||
var testRunner = GameObject.Find(UnitTestRunnerGameObjectName);
|
||||
Assert.IsNotNull(testRunner);
|
||||
GameObject.DontDestroyOnLoad(testRunner);
|
||||
|
||||
// We want to clear all objects across all scenes to ensure the next test is not affected
|
||||
// at all by previous tests
|
||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||
{
|
||||
foreach (var obj in SceneManager.GetSceneAt(i).GetRootGameObjects())
|
||||
{
|
||||
GameObject.DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (ProjectContext.HasInstance)
|
||||
{
|
||||
var dontDestroyOnLoadRoots = ProjectContext.Instance.gameObject.scene
|
||||
.GetRootGameObjects();
|
||||
|
||||
foreach (var rootObj in dontDestroyOnLoadRoots)
|
||||
{
|
||||
if (rootObj.name != UnitTestRunnerGameObjectName)
|
||||
{
|
||||
if (immediate)
|
||||
{
|
||||
GameObject.DestroyImmediate(rootObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject.Destroy(rootObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4101e443fc2ede94c99d4d759087cc44
|
||||
timeCreated: 1527644566
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Zenject
|
||||
{
|
||||
// Inherit from this and mark you class with [TestFixture] attribute to do some unit tests
|
||||
// For anything more complicated than this, such as tests involving interaction between
|
||||
// several classes, or if you want to use interfaces such as IInitializable or IDisposable,
|
||||
// then I recommend using ZenjectIntegrationTestFixture instead
|
||||
// See documentation for details
|
||||
public abstract class ZenjectUnitTestFixture
|
||||
{
|
||||
DiContainer _container;
|
||||
|
||||
protected DiContainer Container
|
||||
{
|
||||
get { return _container; }
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public virtual void Setup()
|
||||
{
|
||||
_container = new DiContainer(StaticContext.Container);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public virtual void Teardown()
|
||||
{
|
||||
StaticContext.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51305c7ccfc8a9f4390aeefc54674cde
|
||||
timeCreated: 1536808207
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user