繁体   English   中英

如何测试仅具有开关逻辑的功能?

[英]How to test a function that has only switch logic?

我有一个Save()函数的代码,里面只有switch语句。 因此,基本上可以根据选择的平台进行节省。 但是我已经对UpdateGameState(),SaveForWeb()和SaveForX86()函数进行了测试。 由于单元测试规则说,如果您的函数中有逻辑,无论它多么简单,都必须测试该函数。

public void Save ()
{
    switch(Helper.BUILD_TYPE)
    {
        case Helper.BUILD_FOR_WEB:
            SaveForWeb();
            break;

        case Helper.BUILD_FOR_WIN_X86:
            SaveForX86();
            break;

        default:
            Debug.Log("Save method: " + Helper.WRONG_BUILD_TYPE_SELECTED_ERR);
            break;
    }
}

在测试中调用test也会破坏测试的隔离规则,因此看来我不得不在其他测试中复制测试逻辑,只是为了检查Save()逻辑在SaveForWeb()和SaveForX86()之后是否一直有效。

在这种情况下,您将如何测试此功能?

我可以在测试中做到这一点:

Helper.BUILD_TYPE = Helper.BUILD_FOR_WEB;

其中BUILD_TYPE是静态的,但不是BUILD_FOR_WEB和BUILD_FOR_WIN_X86的常数。

这是要测试的课程:

using UnityEngine;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

public class SaveLoadGameData : MonoBehaviour
{
    public static SaveLoadGameData gameState;

    public float experience = Helper.DEFAULT_EXPERIENCE;
    public float score = Helper.DEFAULT_SCORE;

    void Awake ()
    {
        Init();
    }

    public void Init()
    {
        if (gameState == null)
        {
            DontDestroyOnLoad(gameObject);
            gameState = this;
        }
        else if (gameState != this)
        {
            Destroy(gameObject);
        }
    }

    public void SaveForWeb ()
    {
        UpdateGameState();
        try
        {
            PlayerPrefs.SetFloat(Helper.EXP_KEY, experience);
            PlayerPrefs.SetFloat(Helper.SCORE_KEY, score);

            PlayerPrefs.Save();
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }
    }

    public void SaveForX86 ()
    {
        UpdateGameState();
        try
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream fs = File.Create(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME);

            GameData data = new GameData();
            data.experience = experience;
            data.score = score;

            bf.Serialize(fs, data);
            fs.Close();
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }
    }

    public void Save ()
    {
        switch(Helper.BUILD_TYPE)
        {
            case Helper.BUILD_FOR_WEB:
                SaveForWeb();
                break;

            case Helper.BUILD_FOR_WIN_X86:
                SaveForX86();
                break;

            case Helper.BUILD_FOR_ANDROID:
                break;

            default:
                Debug.Log("Save method: " + Helper.WRONG_BUILD_TYPE_SELECTED_ERR);
                break;
        }
    }

    public void LoadForWeb ()
    {
        try
        {
            experience = PlayerPrefs.GetFloat(Helper.EXP_KEY, Helper.DEFAULT_EXPERIENCE);
            score = PlayerPrefs.GetFloat(Helper.SCORE_KEY, Helper.DEFAULT_SCORE);
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }
    }

    public void LoadForX86 ()
    {
        try
        {
            if (File.Exists(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME))
            {
                BinaryFormatter bf = new BinaryFormatter();
                FileStream fs = File.Open(Application.persistentDataPath + Helper.GAME_DATA_FILE_NAME, FileMode.Open);
                GameData data = (GameData)bf.Deserialize(fs);

                experience = data.experience;
                score = data.score;

                fs.Close();
            }
            else
            {
                Save();
            }
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }
    }

    public void Load ()
    {
        switch(Helper.BUILD_TYPE)
        {
            case Helper.BUILD_FOR_WEB:
                LoadForWeb();
                break;

            case Helper.BUILD_FOR_WIN_X86:
                LoadForX86();
                break;

            case Helper.BUILD_FOR_ANDROID:
                break;

            default:
                Debug.Log("Load method: " + Helper.WRONG_BUILD_TYPE_SELECTED_ERR);
                break;
        }
        UpdateGameState();
    }

    public void UpdateGameState ()
    {
        gameState.experience = experience;
        gameState.score = score;
    }

    public void ResetGameState ()
    {
        experience = Helper.DEFAULT_EXPERIENCE;
        score = Helper.DEFAULT_SCORE;

        Save();
    }
}

[Serializable]
class GameData
{
    public float experience = Helper.DEFAULT_EXPERIENCE;
    public float score = Helper.DEFAULT_SCORE;
}

注意:我从Save()中删除了UpdateGameState(),现在它既是SaveForWeb()又是SaveForX86()。

如果已经对UpdateGameState(),SaveForWeb()等进行了测试,则不应“双重”测试逻辑。仅应在设置了不同枚举时验证是否已调用方法。 这意味着保存方法本身可能是它自己的一个类,并且对其他方法依赖一个或多个接口。

根据本测试MonoBehavior的教程,我使用单独的类和接口将MonoBehavior功能与其他可测试功能分离

using System;
using UnityEngine;

namespace Assets.Scripts
{
    /// <summary>
    /// Description of ISaveLoadGameData.
    /// </summary>
    public interface ISaveLoadGameData
    {
        void SaveForWeb();
        void SaveForX86();
        void Save();
        void UpdateGameState();
    }
}

using System;
using UnityEngine;

namespace Assets.Scripts
{
    /// <summary>
    /// Description of SaveLoadGameDataController.
    /// </summary>
    [Serializable]
    public class SaveLoadGameDataController : ISaveLoadGameData
    {
        ISaveLoadGameData slgdInterface;
        GameObject gameObject;

        public static SaveLoadGameDataController gameState;

        public float experience = Helper.DEFAULT_EXPERIENCE;
        public float score = Helper.DEFAULT_SCORE;

        public void SetSaveLoadGameData (ISaveLoadGameData slgd)
        {
            slgdInterface = slgd;
        }

        public void SaveForWeb ()
        {
            slgdInterface.SaveForWeb();
        }

        public void SaveForX86 ()
        {
            slgdInterface.SaveForX86();
        }

        public void Save ()
        {
            slgdInterface.Save();
        }

        public void UpdateGameState ()
        {
            slgdInterface.UpdateGameState();
        }
    }
}

这样,我就可以对Save()函数进行干净而简单的测试,如下所示:

[Test]
[Category(Helper.TEST_CATEGORY_SAVE_GAME_STATE)]
public void SaveTest_SetBuildTypeToWebAndRunSave_PassesIfSaveFunctionCalledSaveForWebFunction ()
{
    // arrange
    Helper.BUILD_TYPE = Helper.BUILD_FOR_WEB;
    var slgdController = FakeSaveLoadGameDataController();

    // act
    slgdController.ClearReceivedCalls();
    slgdController.Save();

    // assert
    slgdController.Received().SaveForWeb();
}

[Test]
[Category(Helper.TEST_CATEGORY_SAVE_GAME_STATE)]
public void SaveTest_SetBuildTypeToX86AndRunSave_PassesIfSaveFunctionCalledSaveForX86Function ()
{
    // arrange
    Helper.BUILD_TYPE = Helper.BUILD_FOR_WIN_X86;
    var slgdController = FakeSaveLoadGameDataController();

    // act
    slgdController.ClearReceivedCalls();
    slgdController.Save();

    // assert
    slgdController.Received().SaveForX86();

    Helper.BUILD_TYPE = Helper.BUILD_FOR_WEB;
}

FakeSaveLoadGameDataController()如下所示:

SaveLoadGameDataController FakeSaveLoadGameDataController ()
{
    SaveLoadGameDataController slgdController = Substitute.For<SaveLoadGameDataController>();
    ISaveLoadGameData slgd = Substitute.For<ISaveLoadGameData>();
    slgdController.SetSaveLoadGameData(slgd);

    slgdController.experience = Arg.Is<float>(x => x > 0);
    slgdController.score = Arg.Is<float>(x => x > 0);

    return slgdController;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM