【问题标题】:How To Mock UnityEngine.Object.Destroy() Call?如何模拟 UnityEngine.Object.Destroy() 调用?
【发布时间】:2013-07-01 13:04:39
【问题描述】:

有没有办法验证 UnityEngine.Object.Destroy() 方法是否为特定游戏对象调用?由于 UnityEngine.Object 不是接口,我无法模拟它并检查 Verifiable()。我正在使用 C#、NUnit 和 Moq。

例如:

UnityEngine.Object.Destroy(audioSource);

我发现的唯一东西就是这个How to Mock (with Moq) Unity methods,但这不是我需要的。

感谢您提供有关此主题的任何帮助或更多信息!

我还做的一件事是,我将上面的调用提取到调用接口中,并验证该方法是否被调用,但这样我只是将问题转移到另一层。

public interface IAudioSource
{
    void DestroyUnityObject();
}

然后我可以在那里调用 Unity Destroy 方法。

public void DestroyUnityObject()
{
    UnityEngine.Object.Destroy(mAudioSource);
}

并模拟上层方法调用。

audioSourceMock.Setup(s => s.DestroyUnityObject()).Verifiable();

但正如我所说,这只会将问题放在其他地方,我仍然无法验证 Unity 方法是否被正确调用。

【问题讨论】:

    标签: c# unit-testing nunit unity3d moq


    【解决方案1】:

    在当前状态下,UnityEngine 不支持 Moq 模拟。这是因为 Moq(或任何其他基于 DynamicProxy1 的框架)不能模拟不可覆盖/不可实现的成员(在接口的情况下)。

    最好的办法是按照您的建议创建一个包装器,并将其注入到通常使用UnityEngine 的类中。这样,您可以正确地对这些类进行单元测试。不幸的是,包装器本身仍然无法测试(即 Moq),并且这里什么也做不了,除非您使用支持静态成员模拟的不同隔离框架或使用 UnityEngine 的实际实现(如常规集成测试那样)。

    1 我在我的博文中解释了这个限制背后的一些细节 - How to mock private method?

    【讨论】:

      【解决方案2】:

      我使用以下系统成功地做到了这一点

      public interface IUnityComponentDestroyer
      {
          void Destroy(Component component);
      }
      
      public class UnityComponentDestroyer : IUnityComponentDestroyer
      {
          /// <inheritdoc />
          public void Destroy(Component component)
          {
              if (!Application.isPlaying)
              {
                  Debug.Log($"Destroy called for {component.name} but it's not runtime, so ignoring call.");
                  return;
              }
              Object.Destroy(component);
          }
      }
      

      然后你可以用这样的代码调用它:

          IUnityComponentDestroyer backingFieldComponentDestroyer;
          public IUnityComponentDestroyer ComponentDestroyer
          {
              get
              {
                  backingFieldComponentDestroyer ??= new UnityComponentDestroyer();
                  return backingFieldComponentDestroyer;
              }
              set => backingFieldComponentDestroyer = value;
          }
      
      
      //Then call it with 
      ComponentDestroyer.Destroy(this);
      

      并在这样的测试中使用它:

      IUnityComponentDestroyer fakeDestroyer = Substitute.For<IUnityComponentDestroyer>();
      // do stuff
      fakeDestroyer.Received().Destroy(Arg.Any<CurveAnimator>());
      

      【讨论】:

        猜你喜欢
        • 2012-07-26
        • 2019-05-19
        • 1970-01-01
        • 2015-08-23
        • 1970-01-01
        • 1970-01-01
        • 2010-11-25
        • 2019-12-08
        相关资源
        最近更新 更多