C# 使用返回随机结果的函数进行单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/311807/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 22:42:55  来源:igfitidea点击:

Unit Testing with functions that return random results

c#.netunit-testingxunit.net

提问by Michael Stum

I don't think that this is specific to a language or framework, but I am using xUnit.net and C#.

我不认为这是特定于语言或框架的,但我使用的是 xUnit.net 和 C#。

I have a function that returns a random date in a certain range. I pass in a date, and the returning date is always in range of 1 to 40 years before the given date.

我有一个函数可以返回某个范围内的随机日期。我传入一个日期,返回日期总是在给定日期之前 1 到 40 年的范围内。

Now I just wonder if there is a good way to unit test this. The best approach seems to be to create a loop and let the function run i.e. 100 times and assert that every of these 100 results are in the desired range, which is my current approach.

现在我只是想知道是否有一种很好的方法来对此进行单元测试。最好的方法似乎是创建一个循环并让函数运行 100 次并断言这 100 个结果中的每一个都在所需的范围内,这是我目前的方法。

I also realize that unless I am able to control my Random generator, there will not be a perfect solution (after all, the result IS random), but I wonder what approaches you take when you have to test functionality that returns a random result in a certain range?

我也意识到,除非我能够控制我的随机生成器,否则不会有完美的解决方案(毕竟,结果是随机的),但我想知道当您必须测试返回随机结果的功能时,您会采用什么方法一定的范围?

采纳答案by SquareCog

In addition to testing that the function returns a date in the desired range, you want to ensure that the result is well-distributed. The test you describe would pass a function that simply returned the date you sent in!

除了测试函数是否返回所需范围内的日期之外,您还希望确保结果分布良好。您描述的测试将传递一个函数,该函数仅返回您发送的日期!

So in addition to calling the function multiple times and testing that the result stays in the desired range, I would also try to assess the distribution, perhaps by putting the results in buckets and checking that the buckets have roughly equal numbers of results after you are done. You may need more than 100 calls to get stable results, but this doesn't sound like an expensive (run-time wise) function, so you can easily run it for a few K iterations.

因此,除了多次调用该函数并测试结果是否保持在所需范围内之外,我还会尝试评估分布,也许是将结果放入桶中并检查桶中的结果数量是否大致相同完毕。您可能需要 100 多次调用才能获得稳定的结果,但这听起来不像是一个昂贵的(运行时明智的)函数,因此您可以轻松地将它运行几次 K 次迭代。

I've had a problem before with non-uniform "random" functions.. they can be a real pain, it's worth testing for early.

我以前遇到过非均匀“随机”函数的问题..它们可能是一个真正的痛苦,值得尽早测试。

回答by flolo

Normaly I use exactly your suggested approach: Control the Random generator. Initialize it for test with a default seed (or replace him by a proxy returning numbers which fit my testcases), so I have deterministic/testable behaviour.

通常,我完全使用您建议的方法:控制随机生成器。用默认种子初始化它以进行测试(或用返回适合我的测试用例的数字的代理替换他),所以我有确定性/可测试的行为。

回答by Ned Batchelder

You don't need to control the system to make the results deterministic. You're on the right approach: decide what is important about the output of the function and test for that. In this case, it is important that the result be in a range of 40 days, and you are testing for that. It's also important that it not always return the same result, so test for that too. If you want to be fancier, you can test that the results pass some kind of randomness test..

您不需要控制系统来使结果具有确定性。您采用了正确的方法:确定函数输出的重要内容并对其进行测试。在这种情况下,重要的是结果在 40 天的范围内,您正在为此进行测试。同样重要的是它并不总是返回相同的结果,所以也要测试一下。如果你想更发烧友,你可以测试结果是否通过了某种随机性测试。

回答by Daniel Rikowski

If you want to check the quality of the random numbers (in terms of independance) there are several ways to do this. One good way is the Chi square test.

如果您想检查随机数的质量(就独立性而言),有几种方法可以做到这一点。一种好方法是卡方检验

回答by Brian Genisio

Mock or fake out the random number generator

模拟或伪造随机数生成器

Do something like this... I didn't compile it so there might be a few syntax errors.

做这样的事情...我没有编译它所以可能有一些语法错误。

public interface IRandomGenerator
{
    double Generate(double max);
}

public class SomethingThatUsesRandom
{
    private readonly IRandomGenerator _generator;

    private class DefaultRandom : IRandomGenerator
    {
        public double Generate(double max)
        {
            return (new Random()).Next(max);
        }
    }

    public SomethingThatUsesRandom(IRandomGenerator generator)
    {
        _generator = generator;
    }

    public SomethingThatUsesRandom() : this(new DefaultRandom())
    {}

    public double MethodThatUsesRandom()
    {
        return _generator.Generate(40.0);
    }
}

In your test, just fake or mock out the IRandomGenerator to return something canned.

在您的测试中,只需伪造或模拟 IRandomGenerator 即可返回一些罐头。

回答by Robert Rossney

I think there are three different aspects of this problem that you test.

我认为您要测试这个问题的三个不同方面。

The first one: is my algorithm the right one? That is, given a properly-functioning random-number generator, will it produce dates that are randomly distributed across the range?

第一个:我的算法是正确的吗?也就是说,给定一个正常运行的随机数生成器,它会生成在整个范围内随机分布的日期吗?

The second one: does the algorithm handle edge cases properly? That is, when the random number generator produces the highest or lowest allowable values, does anything break?

第二个:算法是否正确处理边缘情况?也就是说,当随机数生成器产生最高或最低允许值时,是否有任何中断?

The third one: is my implementation of the algorithm working? That is, given a known list of pseudo-random inputs, is it producing the expected list of pseudo-random dates?

第三个:我的算法实现有效吗?也就是说,给定一个已知的伪随机输入列表,它是否产生了预期的伪随机日期列表?

The first two things aren't something I'd build into the unit-testing suite. They're something I'd prove out while designing the system. I'd probably do this by writing a test harness that generated a zillion dates and performed a chi-square test, as daniel.rikowski suggested. I'd also make sure this test harness didn't terminate until it handled both of the edge cases (assuming that my range of random numbers is small enough that I can get away with this). And I'd document this, so that anyone coming along and trying to improve the algorithm would know that that's a breaking change.

前两件事不是我会在单元测试套件中构建的东西。它们是我在设计系统时证明的东西。正如 daniel.rikowski 所建议的那样,我可能会通过编写一个生成无数日期并执行卡方测试的测试工具来做到这一点。我还要确保这个测试工具在它处理了两个边缘情况之前不会终止(假设我的随机数范围足够小,我可以摆脱这个)。而且我会记录下来,以便任何前来尝试改进算法的人都知道这是一个突破性的变化。

The last one issomething I'd make a unit test for. I need to know that nothing has crept into the code that breaks its implementation of this algorithm. The first sign I'll get when that happens is that the test will fail. Then I'll go back to the code and find out that someone else thought that they were fixing something and broke it instead. If someone didfix the algorithm, it'd be on them to fix this test too.

最后一个我要进行单元测试的东西。我需要知道代码中没有任何内容会破坏该算法的实现。当这种情况发生时,我得到的第一个迹象是测试将失败。然后我将返回代码并发现其他人认为他们正在修复某些东西并破坏了它。如果有人确实修复了算法,那么他们也应该修复这个测试。

回答by mseery

Depending on how your function creates the random date, you may also want to check for illegal dates: impossible leap years, or the 31st day of a 30-day month.

根据您的函数创建随机日期的方式,您可能还想检查非法日期:不可能的闰年或 30 天月份的第 31 天。

回答by philant

Methods that do not exhibit a deterministic behaviorcannot be properly unit-tested,as the results will differ from one execution to another. One way to get around this is to seedthe random number generator with a fixed value for the unit test. You can also extract the randomness of the date generation class (and thus applying the Single Responsibility Principle), and inject known values for the unit-tests.

不表现出确定性行为的方法无法进行适当的单元测试,因为结果会因一次执行而异。要解决这个问题的方法之一是种子与单元测试的固定值的随机数发生器。您还可以提取日期生成类的随机性(从而应用单一职责原则),并为单元测试注入已知值。

回答by thekingoftruth

Sure, using a fixed seed random number generator will work just fine, but even then you're simply trying to test for that which you cannot predict. Which is ok. It's equivalent to having a bunch of fixed tests. However, remember--test what is important, but don't try to test everything. I believe random tests are a way to try to test everything, and it's not efficient (or fast). You could potentially have to run a great many randomized tests before hitting a bug.

当然,使用固定的种子随机数生成器会很好,但即便如此,您也只是尝试测试无法预测的内容。没关系。这相当于有一堆固定的测试。但是,请记住——测试什么是重要的,但不要尝试测试所有内容。我相信随机测试是一种尝试测试所有内容的方法,但效率不高(或速度不快)。在遇到错误之前,您可能需要运行大量随机测试。

What I'm trying to get at here is that you should simply write a test for each bug you find in your system. You test out edge cases to make sure your function is running even in the extreme conditions, but really that's the best you can do without either spending too much time or making the unit tests slow to run, or simply wasting processor cycles.

我想在这里得到的是,您应该简单地为您在系统中发现的每个错误编写一个测试。您测试边缘情况以确保您的函数即使在极端条件下也能运行,但实际上这是您可以做的最好的事情,既不会花费太多时间,也不会使单元测试运行缓慢,或者只是浪费处理器周期。

回答by user263976

I would recommend overriding the random function. I am unit testing in PHP so I write this code:

我建议覆盖随机函数。我正在用 PHP 进行单元测试,所以我写了这段代码:

// If we are unit testing, then...
if (defined('UNIT_TESTING') && UNIT_TESTING)
{
   // ...make our my_rand() function deterministic to aid testing.
   function my_rand($min, $max)
   {
      return $GLOBALS['random_table'][$min][$max];
   }
}
else
{
   // ...else make our my_rand() function truly random.
   function my_rand($min = 0, $max = PHP_INT_MAX)
   {
      if ($max === PHP_INT_MAX)
      {
         $max = getrandmax();
      }
      return rand($min, $max);
   }
}

I then set the random_table as I require it per test.

然后我根据每次测试的需要设置 random_table。

Testing the true randomness of a random function is a separate test altogether. I would avoid testing the randomness in unit tests and would instead do separate tests and google the true randomness of the random function in the programming language you are using. Non-deterministic tests (if any at all) should be left out of unit tests. Maybe have a separate suite for those tests, that requires human input or much longer running times to minimise the chances of a fail that is really a pass.

测试随机函数的真正随机性完全是一个单独的测试。我会避免在单元测试中测试随机性,而是进行单独的测试,并在您使用的编程语言中搜索随机函数的真正随机性。非确定性测试(如果有的话)应该被排除在单元测试之外。也许有一个单独的测试套件,需要人工输入或更长的运行时间,以最大限度地减少真正通过的失败机会。