我应该如何对执行数组插值的类进行单元测试?

时间:2014-08-20 15:14:30

标签: c# unit-testing interpolation numeric

我使用一个项目,其中一些类执行数值插值,也就是说,在已知位置给定一组点,我可以询问网格节点之间的点的位置,可以这么说。

由于这些方法没有按定义返回确切的值,我想知道我应该如何对它们进行单元测试。

例如,下面的代码测试插值返回一个零数组,如果我给它一个类似的零数组,它可以工作,但我怀疑它是有效的,因为我实际上并没有重新采样,只是再次询问相同的立场。

[TestMethod]
public void Interpolate_ZeroIn_ZeroOut()
{
    var Xvalues = new double[] {1,2,3,4,5,6,7,8,9,10};
    var Yvalues = new double[] {0,0,0,0,0,0,0,0,0,0};

    List<Point> points = 
        Enumerable.Zip(Xvalues, Yvalues, (x,y) => new Point(x,y)).ToList();

    int interpolation_order = 5;

    FourierIterpolator target = new FourierInterpolator(points, interpolation_order);

    var output = Xvalues.Select(p => target.Interpolate(p)).ToList();

    CollectionAssert.AreEqual(output, Yvalues);
}

问题是:通过实际重新采样,新点将在&#34;之间&#34;输入点,所以我不能使用CollectionAssert.AreEqual。此外,对于一些使用平滑的插值方法,数组将不相等,只是近似值。

所以,我的问题是:

  

在测试涉及aproximation / interpolation的数值方法时,建议使用哪些断言?

3 个答案:

答案 0 :(得分:2)

您的插值技术确定性? (确定性表示:如果使用相同的参数调用两次方法,那么在两种情况下它都会返回完全相同的结果吗?)

如果是,

  • 通过您的函数运行一些值,
  • 使用外部方法验证结果是否正确(笔和纸,或某些数学工具),
  • 将这些值添加为测试用例。

这将确保测试用例检测到您的方法的任何更改。一个缺点是,如果您改进插值技术,您的情况将会失败,从而产生不同的结果。这不一定是坏事,因为它会提醒您,您的方法现在会返回不同的结果。

如果插值技术不是确定性的,即,如果它使用某种随机源,您可能会断言这些值在一些合理的误差范围内。

答案 1 :(得分:2)

目前很难看出测试的输入数据是什么。我建议你让测试数据明确且易于查看。此外,我认为您不需要10或100分来验证您的方法是否有效。可能2-3分就足够了:

var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });

接下来,您应提供预期值:

double[] expected = { 0.33, 0.66, 1.66 };

最后 - 检查实际值是否等于或接近预期值:

[TestMethod]
public void Interpolar_ZeroIn_ZeroOut()
{
    var points = new []{ new Point(0,0), new Point(1,0), new Point(2,0) });
    // calculate expected values manually
    double[] expected = { 0.33, 0.66, 1.66 };    
    int ordem = 5;

    var interpolator = new InterpoladorFourier(points, ordem);

    for(int i = 0; i < points.Length; i++)        
       Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X));
}

如果预期数据不准确,那么您可以为断言提供delta:

Assert.AreEqual(expected[i], interpolator.Interpolar(points[i].X), 0.01);

甚至更好 - 您可以创建以非常易读的方式创建点的方法:

var points = CreatePoints("0,0", "1,0", "2,0"); 

使用这个辅助方法:

private Point[] CreatePoints(params string[] points)
{
    List<Point> result = new List<Point>();

    foreach(var s in points)
    {
        var parts = s.Split(',');
        var x = Double.Parse(parts[0]);
        var y = Double.Parse(parts[1]);            
        var point = new Point(x,y);
        result.Add(point);
    }

    return result.ToArray();
}

答案 2 :(得分:0)

我通过谷歌来到这里是因为我自己也在寻找一些答案,但我目前的方法可能比这里提供的方法更好,所以我会分享以供将来参考。

大多数(如果不是全部)插值方法都有相当数量的函数,它们可以完美地或至少达到机器精度。例如三次样条,它的多项式达到 3 次。我只是测试合理数量的具有不同系数、不同间隔、不同点数的多项式......如果它接近机器精度,则计算 RMS 误差并“断言” (比如 rms_err < 10*eps)。准确选择适合此处的多项式/函数/参数需要了解有关特定插值方法的一些知识,可能还有浮点误差,但这是可行的。

另一种方法是与具有相同或相似实现的可用库进行比较,或者直接导出仅使用库的值。例如,我使用可用的 1D 实现测试了我的 2D 插值实现,这些实现使用相同的方法,当然是沿着一个维度。