Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在Unity游戏引擎平台上的单元测试中实例化MonoBehavior对象_C#_.net_Unit Testing_Unity3d - Fatal编程技术网

C# 如何在Unity游戏引擎平台上的单元测试中实例化MonoBehavior对象

C# 如何在Unity游戏引擎平台上的单元测试中实例化MonoBehavior对象,c#,.net,unit-testing,unity3d,C#,.net,Unit Testing,Unity3d,我在Github()上有以下开源项目。我目前正在尝试对使用MSTest框架编写的代码进行单元测试,但所有测试都返回相同的错误消息:“未处理的异常:System.Security.SecurityException:ECall方法必须打包到系统模块中。”这是在我尝试使用NUnit模板进行单元测试时发生的 我已经查过了 找到一些答案,但我没有,因为OP说他的解决方案在调试器区域内有效,但在调试器区域外无效。在我看来,当我看这篇文章时,OP的问题并没有得到解决 之后,我在项目中导入了UnityTest

我在Github()上有以下开源项目。我目前正在尝试对使用MSTest框架编写的代码进行单元测试,但所有测试都返回相同的错误消息:“未处理的异常:System.Security.SecurityException:ECall方法必须打包到系统模块中。”这是在我尝试使用NUnit模板进行单元测试时发生的

我已经查过了 找到一些答案,但我没有,因为OP说他的解决方案在调试器区域内有效,但在调试器区域外无效。在我看来,当我看这篇文章时,OP的问题并没有得到解决

之后,我在项目中导入了UnityTestTools框架。因为它是基于NUnit框架的,所以我认为这很容易。事实证明不是。测试本身是相当基本的。我有一个基类,叫做BaseCharacterClass:MonoBehavior,它具有BaseCharacterStats类型的属性。在统计数据中,有一个CharacterHealth类型的对象,它负责玩家的健康

现在,我有以下两个堆栈跟踪,当我在测试中尝试以下内容时,我似乎没有得到这些跟踪

单元测试(NUNIT)

  • 使用新关键字创建单行为对象

    [Test]
    [Category("Mock Character")]
    public void Mock_Character_With_No_Health()
    {
        var mock = new MoqBaseCharacter ();
        Assert.NotNull (mock.BaseStats);
        Assert.NotNull (mock.BaseStats.Health);
        Assert.LessOrEqual (0, mock.BaseStats.Health.CurrentHealth);
    }
    //This is not the full file
    //There "2" classes: 1 for holding tests and that Mock object 
    public MoqBaseCharacter()
    {
        this.BaseStats = new BaseCharacterStats ();
        this.BaseStats.Health = new CharacterHealth (0);
    }
    
  • 堆栈跟踪

    模拟角色,无生命值(0.047s) ---System.NullReferenceException:对象引用未设置为对象的实例 ---位于中的Assets.Scripts.CharactersUtil.CharacterHealth..ctor(Int32 sh)[0x0002f] C:\Users\Kevin\Documents\AndroidPC\u Prototype\PC\u Augmented\u tractions\u Demo\Assets\Scripts\CharactersUtil\CharacterHealth.cs:29

    在中的UnityTest.MoqBaseCharacter..ctor()[0x00011]处 C:\Users\Kevin\Documents\AndroidPC\u Prototype\PC\u Augmented\u tractics\u Demo\Assets\unitytestools\Examples\UnitTestExamples\Editor\SampleTests.cs:14

    在UnityTest.SampleTests.Mock_Character_中,不包含运行状况()[0x00000]的字符 C:\Users\Kevin\Documents\AndroidPC\u Prototype\PC\u Augmented\u tractics\u Demo\Assets\unitytestools\Examples\UnitTestExamples\Editor\SampleTests.cs:32

  • 使用NSubstitute.For

    [Test]
    [Category("Mock Character")]
    public void Mock_Character_With_No_Health()
    {
        var mock = NSubstitute.Substitute.For<MoqBaseCharacter> ();
        Assert.NotNull (mock.BaseStats);
        Assert.NotNull (mock.BaseStats.Health);
        Assert.LessOrEqual (0, mock.BaseStats.Health.CurrentHealth);
    }
    
    [测试]
    [类别(“模拟角色”)]
    公共无效模拟字符,具有无运行状况()
    {
    var mock=NSubstitute.Substitute.For();
    Assert.NotNull(mock.BaseStats);
    Assert.NotNull(mock.BaseStats.Health);
    Assert.LessOrEqual(0,mock.BaseStats.Health.CurrentHealth);
    }
    
  • 堆栈跟踪

    模拟角色,无生命值(0.137s) ---System.Reflection.TargetInvocationException:调用的目标已引发异常。--> System.NullReferenceException:对象引用未设置为 对象的实例 ---在System.Reflection.MonoCMethod.Invoke(System.Object obj、BindingFlags invokeAttr、System.Reflection.Binder Binder、, System.Object[]参数,System.Globalization.CultureInfo区域性) [0x0012c]英寸 /Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:519

    在System.Reflection.MonoCMethod.Invoke(BindingFlags invokeAttr, System.Reflection.Binder,System.Object[]参数, System.Globalization.CultureInfo culture)[0x00000]位于 /Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:528

    位于System.Activator.CreateInstance(System.Type类型,BindingFlags bindingAttr,System.Reflection.Binder,System.Object[]参数, System.Globalization.CultureInfo区域性,System.Object[] 激活属性)[0x001b8]在 /Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System/Activator.cs:338

    在System.Activator.CreateInstance(System.Type类型,System.Object[] args,System.Object[]activationAttributes)[0x00000]位于 /Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System/Activator.cs:268

    在System.Activator.CreateInstance(System.Type类型,System.Object[] args)[0x00000]英寸 /Users/builduser/buildslave/mono运行时和classlibs/build/mcs/class/corlib/System/Activator.cs:263

    位于Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance (System.Type proxyType、System.Collections.Generic.List`1 proxyArguments,System.Type classToProxy,System.Object[] 0中的构造函数参数[0x00000]

    位于Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(System.Type classToProxy,System.Type[]additionalInterfacesToProxy, Castle.DynamicProxy.ProxyGenerationOptions选项,System.Object[] 构造函数参数,Castle.DynamicProxy.IInterceptor[]拦截器) [0x00000]英寸:0

    在 NSSubstitute.Proxy.CastleDynamicProxy.CastleDynamicProxyFactory.CreateProxyUsingCastleProxyGenerator (System.Type typeToProxy,System.Type[]附加接口, System.Object[]构造函数参数,IInterceptor拦截器, Castle.DynamicProxy.ProxyGeneration选项ProxyGeneration选项) [0x00000]英寸:0

    在 NSubstitute.Proxies.CastleDynamicProxy.CastleDynamicProxyFactory.GenerateProxy (ICallRouter callRouter,System.Type typeToProxy,System.Type[] 附加接口,System.Object[]构造函数参数)[0x00000] 年:0

    在NSubstitute.Proxies.ProxyFactory.GenerateProxy(ICallRouter callRouter,System.Type typeToProxy,System.Type[] 附加接口,System.Object[]构造函数参数)[0x00000] 年:0

    在nssubstitute.Core.SubstituteFactory.Create(System.Type[] typesToProxy,System.Object[]构造函数参数,SubstituteConfig 配置中的[0x00000]:0

    在nssubstitute.Core.SubstituteFactory.Create(System.Type[] typesToProxy,System.Object[]构造函数参数)[0x00000]位于 :0

    在NSubstitute.Substitute.For(System.Type[]typesToProxy)处, 系统对象[]构造
    using System;
    using UnityEngine;
    using System.Collections.Generic;
    using JetBrains.Annotations;
    using Random = System.Random;
    
    namespace Assets.Scripts.CharactersUtil
    {
        public class BaseCharacterClass : MonoBehaviour
        {
            //int[] basicUDLRMovementArray = new int[4];
    
            public List<BaseCharacterClass> CurrentEnnemies; 
            public int StartingHealth = 500;
            public BaseCharacterStats BaseStats { get; set; }
    
            // Use this for initialization
            private void Start()
            {
                BaseStats = new BaseCharacterStats {Health = new CharacterHealth(StartingHealth)}; //Testing purposes
                BaseStats.ChanceForCriticalStrike = new Random().Next(0,BaseStats.CriticalStrikeCounter);
            }
    
            // Update is called once per frame
    
            private void Update()
            {
                //ExecuteBasicMovement();
    
            }
    
            //During an attack with any kind of character
            //TODO: Make sure that people from the same team cannot attack themselves (friendly fire)
            private void OnTriggerEnter([NotNull] Collider other)
            {
                if (other == null) throw new ArgumentNullException(other.tag);
                Debug.Log("I'm about to receive some damage");
                var characterStats = other.gameObject.GetComponent<BaseCharacterClass>().BaseStats;
                var heathToAddOrRemove = other.gameObject.tag == "Healer" || other.gameObject.tag == "AIHealer" ? characterStats.Power : -1 * characterStats.Power;
                characterStats.Health.TakeDamageFromCharacter((int)heathToAddOrRemove);
                Debug.Log("I should have received damage from a bastard");
                if (characterStats.Health.CurrentHealth == 500)
                {
                    Debug.Log("This is a mistake, I believe I'm a god! INVICIBLE");
                }
            }
    
            /*
            public void ExecuteBasicMovement()
            {
                var move = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
                transform.position += move * BaseStats.Speed * Time.deltaTime;
            }
    
            //TODO: Make sure players moves correctly within the environment per cases
            public void ExecuteMovementPerCase()
            {
            }
            */
    
            public bool CanDoExtraDamage()
            {
                if (BaseStats.ChanceForCriticalStrike*BaseStats.Luck < 50) return false;
                BaseStats.CriticalStrikeCounter--;
                BaseStats.ChanceForCriticalStrike = new Random().Next(0, BaseStats.CriticalStrikeCounter);
                BaseStats.AjustCriticalStrikeChances(); 
                return true;
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using JetBrains.Annotations;
    
    namespace Assets.Scripts.CharactersUtil
    {
        public class BaseCharacterStats
        {
            public float Power { get; set; }
            public float Defense { get; set; }
            public float Agility { get; set; }
            public float Speed { get; set; } 
            public float MagicPower { get; set; }
            public float MagicResist { get; set; }
            public int ChanceForCriticalStrike;
            public int Luck { get; set; }
            public int CriticalStrikeCounter = 20;
            public int TemporaryDefenseBonusValue;
            private Random _randomValueGenerator;
    
            public BaseCharacterStats()
            {
                _randomValueGenerator= new Random();
            }
    
            [NotNull]
            public CharacterHealth Health
            {
                get { return _health; }
                set { _health = value; }
            }
            private CharacterHealth _health;
    
            public void AjustCriticalStrikeChances()
            {
                if (CriticalStrikeCounter <= 5)
                {
                    CriticalStrikeCounter = 5;
                }
            }
    
            public int DetermineDefenseBonusForTurn()
            {
                TemporaryDefenseBonusValue = _randomValueGenerator.Next(10,20);
                return TemporaryDefenseBonusValue;
            }
        }
    }
    
    using JetBrains.Annotations;
    using UnityEngine;
    using UnityEngine.UI;
    
    namespace Assets.Scripts.CharactersUtil
    {
        public class CharacterHealth {
            public int StartingHealth { get; set; }
            public int CurrentHealth { get; set; }
            public Slider HealthSlider { get; set; }
            public bool isDead;
            public Color MaxHealthColor = Color.green;
            public Color MinHealthColor = Color.red;
            private int _counter;
            private const int MaxHealth = 200;
            public Image Fill;
    
    
            private void Awake() {
                //HealthSlider = GameObject.GetComponent<Slider>();
                _counter = MaxHealth;            // just for testing purposes
            }
            // Use this for initialization
    
            public CharacterHealth(int sh)
            {
                StartingHealth = sh;
                CurrentHealth = StartingHealth;
                HealthSlider.wholeNumbers = true; 
                HealthSlider.minValue = 0f;
                HealthSlider.maxValue = StartingHealth;
                HealthSlider.value = CurrentHealth; 
            }
    
            public void Start()
            {
                HealthSlider.wholeNumbers = true; 
                HealthSlider.minValue = 0f;
                HealthSlider.maxValue = MaxHealth;
                HealthSlider.value = MaxHealth;  
            }
    
            public void TakeDamageFromCharacter([NotNull] BaseCharacterClass baseCharacter)
            {
                CurrentHealth -= (int)baseCharacter.BaseStats.Power;
                HealthSlider.value = CurrentHealth;
                UpdateHealthBar ();
                if (CurrentHealth <= 0)
                    isDead = true;
            }
    
            public void TakeDamageFromCharacter(int characterStrength)
            {
                CurrentHealth -= characterStrength;
                HealthSlider.value = CurrentHealth;
                UpdateHealthBar ();
                if (CurrentHealth <= 0)
                    isDead = true;
            }
    
            public void RestoreHealth(BaseCharacterClass bs)
            {
                CurrentHealth += (int)bs.BaseStats.Power;
                HealthSlider.value = CurrentHealth;
                UpdateHealthBar ();
            }
            public void RestoreHealth(int characterStrength)
            {
                CurrentHealth += characterStrength;
                HealthSlider.value = CurrentHealth;
                UpdateHealthBar ();
            }
            public void UpdateHealthBar() {
                Fill.color = Color.Lerp(MinHealthColor, MaxHealthColor, (float)CurrentHealth / MaxHealth);
            }
        }
    }
    
    public class BaseCharacterClass 
    {
        public BaseCharacterStats BaseStats { get; set; }
        public BaseCharacterClass(int startingHealth) 
        {
            BaseStats = new BaseCharacterStats {Health = new CharacterHealth(startingHealth)}; //Testing purposes
            BaseStats.ChanceForCriticalStrike = new Random().Next(0,BaseStats.CriticalStrikeCounter);
        }
    
        public bool CanDoExtraDamage() 
        {
            if (BaseStats.ChanceForCriticalStrike*BaseStats.Luck < 50) return false;
            BaseStats.CriticalStrikeCounter--;
            BaseStats.ChanceForCriticalStrike = new Random().Next(0, BaseStats.CriticalStrikeCounter);
            BaseStats.AjustCriticalStrikeChances(); 
            return true;
        }
    }
    
    using System;
    using UnityEngine;
    using System.Collections.Generic;
    using JetBrains.Annotations;
    using Random = System.Random;
    
    namespace Assets.Scripts.CharactersUtil
    {
        public class BaseCharacterClassWrapper : MonoBehaviour
        {
            //int[] basicUDLRMovementArray = new int[4];
    
            public List<BaseCharacterClass> CurrentEnnemies; 
            public int StartingHealth = 500;        
    
            public BaseCharacterClass CharacterClass;
    
    
            public CharacterHealthUI HealthUI;
    
            // Use this for initialization
            private void Start()
            {
                CharacterClass = new BaseCharacterClass(StartingHealth);  
                HealthUI = this.GetComponent<CharacterHealthUI>();
                HealthUI.CharacterHealth = CharacterClass.BaseStats.Health;
            }
    
            // Update is called once per frame
    
            private void Update()
            {
                //ExecuteBasicMovement();
            }
    
            //During an attack with any kind of character
            //TODO: Make sure that people from the same team cannot attack themselves (friendly fire)
            private void OnTriggerEnter([NotNull] Collider other)
            {
                if (other == null) throw new ArgumentNullException(other.tag);
                Debug.Log("I'm about to receive some damage");
    
                var characterStats = other.gameObject.GetComponent<BaseCharacterClassWrapper>().CharacterClass.BaseStats;
    
                var healthToAddOrRemove = other.gameObject.tag == "Healer" || other.gameObject.tag == "AIHealer" ? characterStats.Power : -1 * characterStats.Power;
    
                characterStats.Health.TakeDamageFromCharacter((int)healthToAddOrRemove);
    
                Debug.Log("I should have received damage from a bastard");
    
                if (characterStats.Health.CurrentHealth == 500)
                {
                    Debug.Log("This is a mistake, I believe I'm a god! INVICIBLE");
                }
            }
    
            /*
            public void ExecuteBasicMovement()
            {
                var move = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
                transform.position += move * BaseStats.Speed * Time.deltaTime;
            }
    
            //TODO: Make sure players moves correctly within the environment per cases
            public void ExecuteMovementPerCase()
            {
            }
            */
    
    
    
            public bool CanDoExtraDamage()
            {
                return CharacterClass.CanDoExtraDamage();
            }
        }
    }
    
    using JetBrains.Annotations;
    using UnityEngine;
    using UnityEngine.UI;
    
    namespace Assets.Scripts.CharactersUtil
    {
        public class CharacterHealthUI : MonoBehavior {
          public Image Fill;
          public Color MaxHealthColor = Color.green;
          public Color MinHealthColor = Color.red;   
          public Slider HealthSlider;
    
          private void Start() {
              if(!HealthSlider) {
                HealthSlider = this.GetComponent<Slider>();            
              }
              if(!Fill) {
                Fill = this.GetComponent<Image>();
              }          
          }
    
          private CharacterHealth _charaHealth;
          public CharacterHealth CharacterHealth { 
            get { return _charaHealth; }
            set { 
            if(_charaHealth!=null)
                _charaHealth.HealthChanged -= HealthChanged;
              _charaHealth = value; 
              _charaHealth.HealthChanged += HealthChanged;
            }
          }
    
          public HealthChanged(object sender, HealthChangedEventArgs hp) {
                HealthSlider.wholeNumbers = true; 
                HealthSlider.minValue = hp.MinHealth;
                HealthSlider.maxValue = hp.MaxHealth;
                HealthSlider.value = hp.CurrentHealth;  
                Fill.color = Color.Lerp(MinHealthColor, MaxHealthColor, (float)hp.CurrentHealth / hp.MaxHealth);
          }
    
        }
    
    }
    
    using JetBrains.Annotations;
    using UnityEngine;
    using UnityEngine.UI;
    
    namespace Assets.Scripts.CharactersUtil
    {      
    
        public class HealthChangedEventArgs : EventArgs 
        {
            public float MinHealth { get; set; }
            public float MaxHealth { get; set; }
            public float CurrentHealth { get; set;}
            public HealthChangedEventArgs(float minHealth, float curHealth, float maxHealth) {
                MinHealth = minHealth;
                CurrentHealth = curHealth;
                MaxHealth = maxHealth;
            }
        }
    
    
        public class CharacterHealth {
            public int StartingHealth { get; set; }
    
            private int _currentHealth;
            public int CurrentHealth 
            { 
              get { return _currentHealth; } 
              set { 
                  _currentHealth = value;
                  if(HealthChanged!=null)
                    HealthChanged(this, new HealthChangedEventArgs(0f, _currentHealth, MaxHealth);
                }
            }      
    
            public bool isDead;
    
            private int _counter;
            private const int MaxHealth = 200;
    
            public event EventHandler<HealthChangedEventArgs> HealthChanged;
    
            // Use this for initialization
    
            public CharacterHealth(int sh)
            {
                StartingHealth = sh;
                CurrentHealth = StartingHealth;
            }
    
            public void TakeDamageFromCharacter([NotNull] BaseCharacterClass baseCharacter)
            {
                CurrentHealth -= (int)baseCharacter.BaseStats.Power;        
                if (CurrentHealth <= 0)
                    isDead = true;
            }
    
            public void TakeDamageFromCharacter(int characterStrength)
            {
                CurrentHealth -= characterStrength;
                if (CurrentHealth <= 0)
                    isDead = true;
            }
    
            public void RestoreHealth(BaseCharacterClass bs)
            {
                CurrentHealth += (int)bs.BaseStats.Power;
            }
            public void RestoreHealth(int characterStrength)
            {
                CurrentHealth += characterStrength;
            }
        }
    }
    
    public static class TestableObjectFactory {
        public static T Create<T>() {
            return FormatterServices.GetUninitializedObject(typeof(T)).CastTo<T>();
        }
    }
    
    var testableObject = TestableObjectFactory.Create<MyMonoBehaviour>();
    testableObject.Test();