NUnit TestCaseSource 第一个为空 运行

NUnit TestCaseSource null on first run

使用 NUnit 和 Unity 我有一个基础 class 用于测试:

    [TestFixture]
    public class TestBase
    {
        private string m_filePath = "UnitTest\response.json";

        protected static Event m_current;
        protected static Event m_expired;

        protected LoginResponse m_response;

        public static IEnumerable<TestCaseData> TestRunning
        {
            get
            {
                yield return new TestCaseData(m_expired, false);
                yield return new TestCaseData(m_current, true);
            }
        }

        [OneTimeSetUp]
        public virtual void SetUp()
        {
            string path = System.IO.Path.Combine(Application.dataPath, m_filePath);
            string txt = File.ReadAllText(path);
            m_response = JsonFx.Json.JsonReader.Deserialize<LoginResponse>(txt);

            m_expired = m_response.events[0];
            m_current = m_response.events[1];
        }

        protected class LoginResponse
        {
            public List<Event> events;
        }
    }

然后我在使用 TestRunning 集合的子 class 中有一个 TestCaseSource,重新编译后它将在第一个 运行 测试中失败。如果我 运行 再次测试,它们就会通过。 第一个对象在测试中为空。

sub class 没有设置,因此它不会阻止调用。

有多种方法可以解释 NUnit 在测试中执行各种操作的顺序,具有不同的详细程度。这是可能的最高级别...

  1. 所有与发现测试有关的操作。这包括查找和解释所有提供数据的属性,包括 [TestCase][TestCaseSource][Values]

  2. 与执行已发现的测试有关的所有操作。这包括执行所有 OneTimeSetupSetUp 方法、测试方法本身以及任何 TearDownOneTimeTearDown 方法。

在第 2 组中,关于首先执行哪些特定操作有相当复杂的规则,但即使是最早的操作(例如基础 class OneTimeSetUp 方法)也永远不会在第 1 组中的操作已完成。

这种严格排序的原因是第 1 组中的属性不仅仅提供供 运行 测试使用的数据。他们还确定找到了多少测试。也就是说,具有五组值的 TestCaseSource 创建五个测试,具有 100 组的 TestCaseSource 创建 100 个测试。

理解了这一点,解决起来就很简单了。您的 TestCaseSource 必须实际阅读 json 文件并根据在那里找到的内容创建案例。这意味着您的源可能需要是一个方法而不是一个简单的集合。

根据@Charlie 的回答,这里是解决我的问题所需的修改:

public class TestBase
{
    private string m_filePath = "UnitTest\response.json";

    protected static Event m_current;
    protected static Event m_expired;

    protected static LoginResponse m_response;

    public static IEnumerable<TestCaseData> TestRunning
    {
        get
        {
            if(m_response == null)
            {
                SetUpTest();
            }
            yield return new TestCaseData(m_expired, false);
            yield return new TestCaseData(m_current, true);
        }
    }

    [OneTimeSetUp]
    public virtual void SetUp()
    {
        SetUpTest();
    }

    protected class LoginResponse
    {
        public List<Event> events;
    }
    
    private static void SetUpTest()
    {
        string path = System.IO.Path.Combine(Application.dataPath, m_filePath);
        string txt = File.ReadAllText(path);
        m_response = JsonFx.Json.JsonReader.Deserialize<LoginResponse>(txt);

        m_expired = m_response.events[0];
        m_current = m_response.events[1];
    }
}