【问题标题】:Xunit CSV streamReader.ReadToEnd returns System.ArgumentOutOfRangeExceptionXunit CSV streamReader.ReadToEnd 返回 System.ArgumentOutOfRangeException
【发布时间】:2022-12-12 17:35:55
【问题描述】:

我想用 Xunit 评估 CSV 数据系列。 为此,我需要读入一个由 int、bool、double 等组成的字符串。 使用以下代码,传输基本上适用于一行。 但是因为我想测试前身值,所以我需要一个完整的 CSV 文件来进行评估。 我的 [理论] 使用 InlineData 没有错误。 但是当我读入 CSV 文件时,CSVDataHandler 给出了 System.ArgumentOutOfRangeException!

我找不到错误的解决方案并寻求支持。 非常感谢!

        [Theory, CSVDataHandler(false, "C:\\MyTestData.txt", Skip = "")]
        public void TestData(int[] newLine, int[] GetInt, bool[] GetBool)
        {
            for (int i = 0; i < newLine.Length; i++)
            {
                output.WriteLine("newLine {0}", newLine[i]);
                output.WriteLine("GetInt {0}", GetInt[i]);
                output.WriteLine("GetBool {0}", GetBool[i]);
            }
        }

    [DataDiscoverer("Xunit.Sdk.DataDiscoverer", "xunit.core")]
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]

    public abstract class DataArribute : Attribute
    {
        public abstract IEnumerable<object> GetData(MethodInfo methodInfo);
        public virtual string? Skip { get; set; }
    }

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class CSVDataHandler : DataAttribute
    {
        public CSVDataHandler(bool hasHeaders, string pathCSV)
        {
            this.hasHeaders = hasHeaders;
            this.pathCSV = pathCSV;
        }

        public override IEnumerable<object[]> GetData(MethodInfo methodInfo)
        {
            var methodParameters = methodInfo.GetParameters();
            var paramterTypes = methodParameters.Select(p => p.ParameterType).ToArray();

            using (var streamReader = new StreamReader(pathCSV))
            {
                if (hasHeaders) { streamReader.ReadLine(); }
                string csvLine = string.Empty;

                // ReadLine ++
                //while ((csvLine = streamReader.ReadLine()) != null)
                //{
                //    var csvRow = csvLine.Split(',');
                //    yield return ConvertCsv((object[])csvRow, paramterTypes);
                //}

                // ReadToEnd ??
                while ((csvLine = streamReader.ReadToEnd()) != null)
                {
                    if (Environment.NewLine != null)
                    {
                        var csvRow = csvLine.Split(',');
                        yield return ConvertCsv((object[])csvRow, paramterTypes); // System.ArgumentOutOfRangeException
                    }
                }
            }
        }

        private static object[] ConvertCsv(IReadOnlyList<object> cswRow, IReadOnlyList<Type> parameterTypes)
        {
            var convertedObject = new object[parameterTypes.Count];

            for (int i = 0; i < parameterTypes.Count; i++)
            {
                convertedObject[i] = (parameterTypes[i] == typeof(int)) ? Convert.ToInt32(cswRow[i]) : cswRow[i]; // System.ArgumentOutOfRangeException
                convertedObject[i] = (parameterTypes[i] == typeof(double)) ? Convert.ToDouble(cswRow[i]) : cswRow[i];
                convertedObject[i] = (parameterTypes[i] == typeof(bool)) ? Convert.ToBoolean(cswRow[i]) : cswRow[i];
            }
            return convertedObject;
        }
    }

MyTestData.txt 
1,2,true,
2,3,false,
3,10,true,

【问题讨论】:

  • 这里可能只是一个错字,但在您的代码中,您写的是 public abstract class DataArribute 而不是 DataAttribute-- 两个 r 而不是 ttr。

标签: css xunit streamreader


【解决方案1】:

第一次调用streamReader.ReadToEnd() 将以字符串的形式返回文件的全部内容,而不仅仅是一行。当您调用csvLine.Split(',')时,您将获得一个包含 12 个元素的数组。

streamReader.ReadToEnd() 的第二次调用不会返回null,因为您的while 语句似乎期望返回一个空字符串。请参阅文档

https://learn.microsoft.com/en-us/dotnet/api/system.io.streamreader.readtoend?view=net-7.0

如果当前位置在流的末尾,则返回一个空 细绳 (””)。

对于空字符串,对 call csvLine.Split(',') 的调用将返回一个长度为 0 的数组,这会在访问其第一个元素(索引 0)时导致异常。

所有这些都可以通过简单地在调试器中启动测试来轻松发现。

看起来你在这里也有一些其他问题。

我不明白你的if (Environment.NewLine != null) 打算做什么,NewLine 属性永远不会为 null,但应该具有以下值之一“ “ 或者 ” " 因此 if 将始终被采用。

你的测试方法的参数是数组int[]bool[],但是你正在检查你的ConvertCsv方法中的类型intdoublebool,所以替代的cswRow[i]将永远是回来。您最终会将字符串传递给期望 int[]bool[] 的方法,并且最迟会在那里出现错误。

【讨论】:

  • 你好克里斯托弗,我现在已经尝试了所有已知的问题解决方案变体,但仍然失败。 splitCounter 和对要读取的行的迭代都无法解决问题。部分变量在调试时无法识别或顺序错误..... ReadToEnd 似乎在任何情况下都不成功?除了 CSVDataHandler 之外,还有其他方法可以将数据从 CSV 读取到 Theory 中吗?
  • 您的方法 ConvertCsv 返回一个简单字符串数组,而不是您的测试方法所需的一个 int 和 bool 数组。要一步步进行,首先修改GetData,直接使用c#代码简单构造三个数组newlineGetIntGetBool,例如newline = new int[3]; newline[0] = 1; newline[1] = 2 等,而不是从文件中读取。然后yield new object[] {newline , GetInt, GetBool}。一旦你开始工作,你就可以开始担心如何从你的 CSV 文件中填充这三个数组以便能够生成它们。
【解决方案2】:

此方法从多个行和列中读取数据系列,并将其作为数组返回以用于测试目的。 可以根据现有模式调整列的转换。 感谢克里斯托弗!

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class CSVDataHandler : Xunit.Sdk.DataAttribute
    {
        public CSVDataHandler(string pathCSV)
        {
            this.pathCSV = pathCSV;
        }

        public override IEnumerable<object[]> GetData(MethodInfo methodInfo)
        {
            List<int> newLine = new();
            List<int> GetInt = new();
            List<bool> GetBool = new();
            var reader = new StreamReader(pathCSV);
            string readData = string.Empty;

            while ((readData = reader.ReadLine()) != null)
            {
                string[] split = readData.Split(new char[] { ',' });
                newLine.Add(int.Parse(split[0]));
                GetInt.Add(int.Parse(split[1]));
                GetBool.Add(bool.Parse(split[2]));
                // Add more objects ...
            }
            yield return new object[] { newLine.ToArray(), GetInt.ToArray(), GetBool.ToArray() };
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-27
    • 2011-02-04
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多