Skip to content

Commit

Permalink
feat(testing): rework TestBed to work with any class
Browse files Browse the repository at this point in the history
BREAKING CHANGE: TestBed’s CreateComponent method has been renamed to Instantiate and it no longer takes a factory predicate. Its CreateComponentAsync method has been removed as well as it wasn’t useful in the synchronous Unity Test Framework.
  • Loading branch information
jonisavo committed Jul 8, 2023
1 parent ab03ebe commit f7b4dd5
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 178 deletions.
8 changes: 4 additions & 4 deletions Assets/UIComponents.Tests/LayoutAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void TearDown()
public IEnumerator Given_Layout_Is_Loaded()
{
var testBed = new TestBed<UIComponentWithLayout>().WithSingleton(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();
_mockResolver.Received().LoadAsset<VisualTreeAsset>("Assets/MyAsset.uxml");
}
Expand All @@ -53,7 +53,7 @@ public IEnumerator Given_Layout_Is_Loaded()
public IEnumerator Superclass_Layout_Is_Loaded_If_It_Is_Not_Overridden()
{
var testBed = new TestBed<InheritedComponentWithoutAttribute>().WithSingleton(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();
_mockResolver.Received().LoadAsset<VisualTreeAsset>("Assets/MyAsset.uxml");
}
Expand All @@ -62,7 +62,7 @@ public IEnumerator Superclass_Layout_Is_Loaded_If_It_Is_Not_Overridden()
public IEnumerator Superclass_Layout_Is_Not_Loaded_If_Overridden()
{
var testBed = new TestBed<InheritedComponentWithAttribute>().WithSingleton(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();
_mockResolver.Received().LoadAsset<VisualTreeAsset>("Assets/MyOtherAsset.uxml");
_mockResolver.DidNotReceive().LoadAsset<VisualTreeAsset>("Assets/MyAsset.uxml");
Expand All @@ -72,7 +72,7 @@ public IEnumerator Superclass_Layout_Is_Not_Loaded_If_Overridden()
public IEnumerator Null_Layout_Is_Handled()
{
var testBed = new TestBed<UIComponentWithNullLayout>().WithSingleton(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();
}
}
Expand Down
8 changes: 4 additions & 4 deletions Assets/UIComponents.Tests/ProvideAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void Provides_Dependencies_Automatically()
{
var testBed = new TestBed<ComponentWithDependencies>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
Assert.That(component.StringProperty, Is.InstanceOf<StringClass>());
Assert.That(component.FloatProperty, Is.InstanceOf<FloatClass>());
}
Expand All @@ -62,7 +62,7 @@ public void Allows_Providing_Dependencies_With_A_Cast()
{
var testBed = new TestBed<ComponentWithDependencies>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
Assert.That(component.FloatClassInstance, Is.InstanceOf<FloatClass>());
}

Expand All @@ -81,7 +81,7 @@ public void Logs_Error_When_Provider_Is_Missing()
{
var testBed = new TestBed<ComponentWithInvalidDependency>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
Assert.That(component.StringProperty, Is.Null);
_mockLogger.Received().LogError("Could not provide IStringProperty to StringProperty", component);
}
Expand All @@ -91,7 +91,7 @@ public void Logs_Error_On_Invalid_Cast()
{
var testBed = new TestBed<ComponentWithInvalidDependency>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
Assert.That(component.StringClassInstance, Is.Null);
_mockLogger.Received().LogError("Could not cast IFloatProperty to StringClass", component);
}
Expand Down
8 changes: 4 additions & 4 deletions Assets/UIComponents.Tests/QueryAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public IEnumerator Should_Populate_Fields()
{
var testBed = new TestBed<ComponentWithQueryAttribute>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

Assert.That(component.HelloWorldLabel, Is.InstanceOf<Label>());
Expand Down Expand Up @@ -86,7 +86,7 @@ public IEnumerator Should_Populate_Inherited_Fields()
{
var testBed = new TestBed<ChildComponentWithQueryAttribute>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

Assert.That(component.HelloWorldLabel, Is.InstanceOf<Label>());
Expand All @@ -113,7 +113,7 @@ public IEnumerator Should_Not_Populate_Invalid_Fields()
{
var testBed = new TestBed<ComponentWithInvalidQueryAttribute>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

Assert.That(component.InvalidField, Is.Null);
Expand All @@ -138,7 +138,7 @@ public IEnumerator Should_Log_Errors_If_Query_Yields_No_Results()
{
var testBed = new TestBed<ComponentWithMissingFields>()
.WithSingleton(_mockLogger);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

Assert.That(component.label, Is.Null);
Expand Down
2 changes: 1 addition & 1 deletion Assets/UIComponents.Tests/QueryClassAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public IEnumerator UnitySetUp()
_mockLogger = Substitute.For<ILogger>();
var testBed = new TestBed<QueryClassTestComponent>()
.WithSingleton(_mockLogger);
_queryClassTestComponent = testBed.CreateComponent();
_queryClassTestComponent = testBed.Instantiate();
yield return _queryClassTestComponent.Initialize().AsEnumerator();
}

Expand Down
6 changes: 3 additions & 3 deletions Assets/UIComponents.Tests/StylesheetAttributeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public IEnumerator Given_Stylesheets_Are_Loaded()
var testBed = new TestBed<UIComponentWithTwoStylesheets>()
.WithSingleton(_mockLogger)
.WithTransient(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

_mockResolver.Received().LoadAsset<StyleSheet>("Assets/StylesheetOne.uss");
Expand All @@ -57,7 +57,7 @@ public IEnumerator Inherited_Stylesheets_Are_Loaded()
var testBed = new TestBed<InheritedComponent>()
.WithSingleton(_mockLogger)
.WithTransient(_mockResolver);
var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

_mockResolver.Received().LoadAsset<StyleSheet>("Assets/StylesheetOne.uss");
Expand All @@ -76,7 +76,7 @@ public IEnumerator Invalid_Stylesheets_Output_Error_Message()
.WithSingleton(_mockLogger)
.WithTransient(_mockResolver);

var component = testBed.CreateComponent();
var component = testBed.Instantiate();
yield return component.Initialize().AsEnumerator();

_mockResolver.Received().LoadAsset<StyleSheet>("Assets/StylesheetOne.uss");
Expand Down
91 changes: 41 additions & 50 deletions Assets/UIComponents.Tests/TestBedTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using NSubstitute;
using NUnit.Framework;
using UIComponents.Internal;
using UIComponents.DependencyInjection;
using UIComponents.Testing;
using UIComponents.Tests.Utilities;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UIElements;

namespace UIComponents.Tests
Expand Down Expand Up @@ -64,27 +62,20 @@ public void SetUp()
[Test]
public void Allows_Creating_Component_With_Singleton_Dependencies()
{
var component = _testBed.CreateComponent();
var component = _testBed.Instantiate();
Assert.That(component.GetDependency(), Is.SameAs(_dependencyInstance));
}

[Test]
public void Allows_Creating_Component_With_Transient_Dependencies()
{
var component = _testBed.CreateComponent();
var component = _testBed.Instantiate();
Assert.That(component.GetTransientDependency(), Is.SameAs(_transientDependencyInstance));

var newComponent = _testBed.CreateComponent();
var newComponent = _testBed.Instantiate();
Assert.That(newComponent.GetTransientDependency(), Is.SameAs(_transientDependencyInstance));
}

[Test]
public void Allows_Creating_Component_With_Predicate()
{
var component = _testBed.CreateComponent(() => new Component(true));
Assert.That(component.Value, Is.True);
Assert.That(component.GetDependency(), Is.SameAs(_dependencyInstance));
}

[Test]
public void Allows_Fetching_Dependencies()
Expand All @@ -103,50 +94,50 @@ public void Allows_Changing_Singleton_Instance()
Assert.That(dependency, Is.SameAs(newDependency));
}

[UnityTest]
public IEnumerator Allows_Fetching_Component_With_Task()
[Dependency(typeof(ILogger), provide: typeof(DebugLogger))]
private abstract class Service : IDependencyConsumer
{
var componentTask = _testBed.CreateComponentAsync(() => new Component(true));

yield return componentTask.AsEnumerator();

var component = componentTask.Result;

Assert.That(component.Value, Is.True);
Assert.That(component.GetDependency(), Is.SameAs(_dependencyInstance));
protected readonly ILogger Logger;
private readonly DependencyInjector _dependencyInjector;

var anotherComponentTask = _testBed.CreateComponentAsync();
protected Service()
{
DiContext.Current.RegisterConsumer(this);
_dependencyInjector = DiContext.Current.GetInjector(GetType());
Logger = Provide<ILogger>();
UIC_PopulateProvideFields();
}

yield return componentTask.AsEnumerator();

var anotherComponent = anotherComponentTask.Result;

Assert.That(anotherComponent.Value, Is.False);
Assert.That(anotherComponent.GetDependency(), Is.SameAs(_dependencyInstance));
protected T Provide<T>() where T : class
{
return _dependencyInjector.Provide<T>();
}

public abstract IEnumerable<IDependency> GetDependencies();

protected virtual void UIC_PopulateProvideFields() {}
}


[Test]
public void Allows_Setting_Timeout_For_Async_Operations()
[Dependency(typeof(IMockDependency), provide: typeof(Dependency))]
[Dependency(typeof(ITransientDependency), provide: typeof(TransientDependency), Scope.Transient)]
private partial class TestService : Service
{
_mockResolver.LoadAsset<VisualTreeAsset>("Foo")
.Returns(Task.Delay(1000).ContinueWith(_ => ScriptableObject.CreateInstance<VisualTreeAsset>()));
_testBed.WithAsyncTimeout(TimeSpan.Zero);
[Provide]
public IMockDependency Dependency;

TestBedTimeoutException exception = null;
public ITransientDependency GetTransientDependency() => Provide<ITransientDependency>();
}

try
{
var task = _testBed.CreateComponentAsync();
task.GetAwaiter().GetResult();
}
catch (TestBedTimeoutException ex)
{
exception = ex;
}

Assert.That(exception, Is.Not.Null);
Assert.That(exception.Message, Is.EqualTo("Creation of component Component timed out after 0ms."));
[Test]
public void Works_On_Non_UIComponent_Type()
{
var testBed = new TestBed<TestService>()
.WithSingleton<IMockDependency>(_dependencyInstance)
.WithTransient<ITransientDependency>(_transientDependencyInstance);

var service = testBed.Instantiate();
Assert.That(service.Dependency, Is.SameAs(_dependencyInstance));
Assert.That(service.GetTransientDependency(), Is.SameAs(_transientDependencyInstance));
}
}
}
2 changes: 1 addition & 1 deletion Assets/UIComponents.Tests/UIComponentNoAttributesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public IEnumerator UnitySetUp()
.Returns(Task.FromResult<StyleSheet>(null));
var testBed = new TestBed<UIComponentNoAttributes>()
.WithSingleton(_mockResolver);
_component = testBed.CreateComponent();
_component = testBed.Instantiate();
yield return _component.Initialize().AsEnumerator();
}

Expand Down
22 changes: 11 additions & 11 deletions Assets/UIComponents.Tests/UIComponentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void SetUp()
[Test]
public void Sets_The_Initialized_Value()
{
var component = _testComponentTestBed.CreateComponent();
var component = _testComponentTestBed.Instantiate();
component.Initialize();
Assert.That(component.Initialized, Is.False);

Expand All @@ -77,7 +77,7 @@ public void Sets_The_Initialized_Value()
[Test]
public void Completes_Initialization_Task()
{
var component = _testComponentTestBed.CreateComponent();
var component = _testComponentTestBed.Instantiate();
component.Initialize();

Assert.That(component.InitializationTask.IsCompleted, Is.False);
Expand All @@ -93,7 +93,7 @@ public void Completes_Initialization_Task()
[UnityTest]
public IEnumerator Allows_Waiting_For_Initialization()
{
var component = _testComponentTestBed.CreateComponent();
var component = _testComponentTestBed.Instantiate();
component.Initialize();

Assert.That(component.Initialized, Is.False);
Expand All @@ -117,15 +117,15 @@ private partial class NestedChildComponent : UIComponent {}
[Test]
public void Does_Not_Initialize_If_Children_Are_Uninitialized()
{
var component = _testComponentTestBed.CreateComponent();
var component = _testComponentTestBed.Instantiate();
component.Initialize();

var childTestBed = new TestBed<ChildComponent>()
.WithSingleton<IAssetResolver>(_mockAssetResolver);

var firstChild = childTestBed.CreateComponent();
var firstChild = childTestBed.Instantiate();
firstChild.Initialize();
var secondChild = childTestBed.CreateComponent();
var secondChild = childTestBed.Instantiate();
secondChild.Initialize();

component.Add(firstChild);
Expand All @@ -141,21 +141,21 @@ public void Does_Not_Initialize_If_Children_Are_Uninitialized()
[Test]
public void Initializes_When_Children_Are_Initialized()
{
var component = _testComponentTestBed.CreateComponent();
var component = _testComponentTestBed.Instantiate();
component.Initialize();

var childTestBed = new TestBed<ChildComponent>()
.WithSingleton<IAssetResolver>(_mockAssetResolver);

var firstChild = childTestBed.CreateComponent();
var firstChild = childTestBed.Instantiate();
firstChild.Initialize();
var secondChild = childTestBed.CreateComponent();
var secondChild = childTestBed.Instantiate();
secondChild.Initialize();

var nestedChildTestBed = new TestBed<NestedChildComponent>()
.WithSingleton<IAssetResolver>(_mockAssetResolver);

var nestedChild = nestedChildTestBed.CreateComponent();
var nestedChild = nestedChildTestBed.Instantiate();
nestedChild.Initialize();

firstChild.Add(nestedChild);
Expand Down Expand Up @@ -218,7 +218,7 @@ public IEnumerator Asynchronous_Initialization_Happens_Once()
var testBed = new TestBed<InitCounterComponent>()
.WithSingleton<IAssetResolver>(_mockAssetResolver);

var component = testBed.CreateComponent();
var component = testBed.Instantiate();

component.Initialize();
component.Initialize();
Expand Down
4 changes: 2 additions & 2 deletions Assets/UIComponents/Core/UIComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ private void OnFirstAttachToPanel(AttachToPanelEvent evt)
}

/// <summary>
/// Starts the initialization of the UIComponent. Does nothing if the UIComponent has already been initialized
/// or if initialization is already ongoing.
/// Starts the initialization of the UIComponent. Resolves immediately if the UIComponent
/// has already been initialized or if initialization is already ongoing.
/// </summary>
/// <remarks>
/// This method is called automatically when the UIComponent is first attached to a panel.
Expand Down
Loading

0 comments on commit f7b4dd5

Please sign in to comment.