0

I am working on a Unity project using the Extenject / Zenject dependency injection framework, and I am having trouble writing unit tests for custom prefab factories.

I have an ArchitectController that needs to instantiate different prefab variants that all have the same Placeable component attached. It does this by referencing a Scriptable Object that references the respective prefab. The Placeable component takes in a Direction and a IGameTile as it's own dependencies, so they are passed to the factory too.

// in Placeable.cs
[Inject]
public void Construct(Direction buildingOrientation, IGameTile host) { ... }

I created my custom factory:

public class CustomPlaceableFactory : IFactory<PlaceableSO, Direction, IGameTile, Placeable>
{

    private DiContainer _container;

    public CustomPlaceableFactory(DiContainer container)
    {
        _container = container;
    }

    public Placeable Create(PlaceableSO placeableSO, Direction buildDirection, IGameTile hostingTile)
    {
        return _container.InstantiatePrefabForComponent<Placeable>(placeableSO.content, new object[]
        {
            buildDirection, hostingTile
        });
    }
}

and then implemented it into the ArchitectController class like so:

// in ArchitectController.cs
public void PlaceContentAtTile(PlaceableSO currentContentSO, IGameTile hostTile)
{
    var direction = CurrentBuildingOrientation;
    var newContent = _placeablePlaceableFactory.Create(currentContentSO, direction, hostTile);
    hostTile.Content = newContent;
}

Now in runtime, this works great!

But when I wanted to write a unit test for this piece of code, I ran into some issues... Namely,

Assert hit! Error occurred while instantiating object of type 'Placeable'. Instantiator should not be used to create new mono behaviours. Must use InstantiatePrefabForComponent, InstantiatePrefab, or InstantiateComponent.

Which in my eyes is weird, because my factory is making use of InstantiatePrefabForComponent?

I think I might be missing something. If anyone has any ideas as to what might be causing this, I'd be keen to know.

Here is the test installer with a comment that indicates the line that throws the assertion. The stacktrace then leads to the create method of my custom factory.

public class ArchitectControllerTestInstaller : Installer<ArchitectControllerTestInstaller>
{
    public override void InstallBindings()
    {
        Container.Bind<IGridManager>().FromSubstitute();

        // bind the factory
        Container.BindFactory<PlaceableSO, Direction, IGameTile, Placeable, Placeable.Factory>();
        Container.Bind<IArchitectController>().To<ArchitectController>().AsSingle();
        
        Container.Bind<IGameTile>().FromSubstitute();
        Container.Bind<IGameTileContent>().FromSubstitute();

        // bind the scriptable object that we will get the to be instantiated prefab from
        Container.Bind<PlaceableSO>().FromScriptableObjectResource("Scriptable Objects/Placeable Blue").AsSingle();
    }
}

// BaseUnitTestFixture just calls `Container.Inject(this)` after the test setup
// it extends from ZenjectUnitTestFixture and adds no other behaviour other than
// ensuring I don't forget to call it.
public class ArchitectControllerTest : BaseUnitTestFixture 
{
    protected override void TestSetup() 
    {
        ArchitectControllerTestInstaller.Install(Container);
    }

    [Inject] private IArchitectController _controller;
    [Inject] private IGameTile _gameTile;
    [Inject] private PlaceableSO _content;

    [Test]
    public void PlaceContentAtTile()
    {
        // assert hit on this line:
        _controller.PlaceContentAtTile(_content, _gameTile);
    }
    
}
KamielDev
  • 481
  • 3
  • 13

0 Answers0