在某些情况下,场景测试中未调用 Zenject Mono Installer

Zenject Mono Installer not called in Scene tests under some circumstances

我有一个 "Main" 场景,其中包含一个由玩家控制的 softozor 游戏对象。这是一只活泼的恐龙。作为那个 softozor 游戏对象的子对象,我设置了另一个游戏对象 Installer,由一个 Transform 组件和一个 PlayerInstaller (Script) 组件组成:

PlayerInstaller 安装播放器逻辑所需的一切。最后,在 softozor 游戏对象中,我添加了一个 Game Object Context (Script) 并在其中注册了 PlayerInstaller:

除了softozor游戏对象,我还定义了一个SceneContext:

您会注意到 SceneContext 中的所有安装程序列表都是空的。但是,如果 SceneContext 没有注册任何内容,则 PlayerInstaller 不会被触发。使用该设置玩游戏效果非常好,即 PlayerInstaller 被调用,我可以控制我的恐龙在我的游戏中做任何我想做的事。

到目前为止,还不错。现在,考虑以下场景测试:

public class PlayerTests : SceneTestFixture
{
  [Inject]
  private IPlayer _player;

  [UnityTest]
  public IEnumerator TestScene()
  {
    yield return LoadScene("Main");

    _player.Flap();
    yield return new WaitForSeconds(0.01f);
    [...]
  }
}

在该测试中,_player 成员变量未注入满足 IPlayer 约定的对象。事实上,PlayerInstaller.InstallBindings() 并没有被调用。

相反,如果我在 softozor 游戏对象中删除 Game Object Context (Script) 组件,并在 SceneContext 中注册 PlayerInstaller:

然后我也可以玩游戏了,和以前一样,我的测试是运行,即在我的场景测试中调用了PlayerInstaller.InstallBindings()方法。

我第一次尝试在 softozor 游戏对象上下文中注册 PlayerInstaller 有什么问题?

我正在与

合作

所以你有两个容器,SceneContext 和一个 GameObjectContext

我认为这里发生的是 它们都安装了,但是您的 GameObjectContext 没有添加到 SceneContext - 它一直有效直到SceneContext 实际上需要了解 GameObjectContext(在您的场景测试中就是这种情况)。

不知道什么是 IPlayer 以及您希望在那里注入什么,很难给出更精确的指示,但它没有注入您的 SceneContext,而只注入您的是有道理的GameObjectContext.

通过将 PlayerInstaller 放入 SceneContext 的 MonoInstallers 列表中,您在技术上解决了该问题,但这显然不是您想要的,因为它会使子容器无用并破坏任何类型你想要的分离。

相反,您需要使用外观连接两个上下文Sub-containers and Facades: Using GameObjectContexts (解释包含一个示例,因此它不会对我来说引用它的一部分是有意义的,但它很详细而且很有帮助)

感谢 hugo,我开始明白如何制作它了 运行。我目前的游戏组件设置如下:

我的PlayerInstaller是这样定义的:

public class PlayerInstaller : MonoInstaller
{
  [SerializeField]
  Settings _settings;

  public override void InstallBindings()
  {
    Container.BindInterfacesTo<Softozor>()
      .AsSingle()
      .WithArguments(_settings.Rigidbody);
    Container.BindInterfacesTo<InputController>().AsSingle();
    Container.Bind<InputState>().AsSingle();
    Container.BindInterfacesTo<PlayerInputHandler>().AsSingle();
    Container.BindInterfacesTo<PlayerMoveHandler>().AsSingle();
  }

  [Serializable]
  public class Settings
  {
    public Rigidbody2D Rigidbody;
  }
}

阅读了 hugo 建议的文档后,我检查了在场景测试期间创建了哪些上下文。我能够看到在我的场景测试中调用了方法 PlayerInstaller.InstallBindings。此外,我的 Softozor 实现 IPlayer 接口的对象也被注入到 PlayerMoveHandler class 中。这意味着 Game Object Context 在场景测试期间已成功构建。但是,在该上下文中注册的对象在 Scene Context 中不可用,这是我可以在场景测试中访问的对象。我天真地假设我在我的游戏对象上下文中注册的任何东西都可以在我的场景测试中检索到。然而,很明显,我只能访问在场景上下文中注册的对象。

到那时,如果我真的想访问实现 IPlayer 的实例,我有一个选择:要么我在 softozor 游戏对象上创建一个外观并使 IPlayer 公开可用,或者我找到一种方法来解析游戏对象上下文中的实例。后者有可能吗?

最后,我想我会以不需要访问类型 IPlayer 的实例的方式重新设计我的场景测试,这在我的情况下是完全可能的。但我很想知道是否可以在场景测试中从游戏对象上下文中解析对象。我该怎么做?