본질적으로 랜덤 / 비 결정적 알고리즘의 단위 테스트


41

필자의 현재 프로젝트는 간결하게 "무제한 무작위 이벤트"의 생성과 관련이있다. 기본적으로 검사 일정을 생성 중입니다. 그들 중 일부는 엄격한 일정 제약 조건을 기반으로합니다. 금요일 오전 10시에 일주일에 한 번 검사를 수행합니다. 다른 검사는 "무작위"입니다. "1 주일에 3 번씩 검사해야 함", "9 AM-9PM 시간 사이에 검사해야 함"및 "동일한 8 시간 내에 두 번의 검사가 없어야 함"과 같은 기본 구성 가능한 요구 사항이 있지만 특정 검사 세트에 대해 구성된 제약 조건 내에서 결과 날짜와 시간을 예측할 수 없어야합니다.

단위 테스트 및 TDD, IMO는이 시스템에서 전체 요구 사항 세트가 여전히 불완전한 상태에서 점진적으로 빌드하는 데 사용될 수 있기 때문에이 시스템에서 큰 가치를 지니고 있습니다. 현재 내가 필요하다는 것을 모른다. 엄격한 일정은 TDD의 한 조각 케이크였습니다. 그러나 시스템의 임의 부분에 대한 테스트를 작성할 때 테스트 대상을 실제로 정의하기가 어렵습니다. 스케줄러에 의해 생성 된 모든 시간이 제약 조건 내에 있어야한다고 주장 할 수 있지만 실제 시간이 "무작위"가 아닌 모든 테스트를 통과하는 알고리즘을 구현할 수 있습니다. 사실 그것은 정확히 일어난 일입니다. 시간은 정확하게 예측할 수는 없지만 허용 가능한 날짜 / 시간 범위의 작은 하위 집합으로 분류되는 문제를 발견했습니다. 알고리즘은 여전히 ​​내가 합리적으로 만들 수 있다고 생각한 모든 주장을 통과했으며 그 상황에서 실패하는 자동화 된 테스트를 설계 할 수 없지만 "더 무작위적인"결과가 주어지면 통과합니다. 여러 번 반복하도록 기존의 일부 테스트를 재구성하여 문제가 해결되었음을 입증하고 생성 된 시간이 전체 허용 범위 내에 있는지 시각적으로 확인했습니다.

비 결정적 행동을 기대해야하는 테스트 설계에 대한 팁이 있습니까?


제안에 대한 모든 감사합니다. 결정론적이고 반복 가능하며 확실한 결과를 얻으려면 결정 론적 테스트가 필요 하다고 생각합니다 . 맞는 말이다.

제약 프로세스 (길이가 길 수있는 바이트 배열이 최소값과 최대 값 사이의 길이가되는 프로세스)에 대한 후보 알고리즘을 포함하는 "샌드 박스"테스트 세트를 만들었습니다. 그런 다음 FOR 루프를 통해 해당 코드를 실행하여 알고리즘에 알려진 바이트 배열 (시작부터 1 ~ 10,000,000 사이의 값)을 제공하고 알고리즘은 각각 1009와 7919 사이의 값으로 제한합니다 (소수점을 사용하여 알고리즘은 입력 범위와 출력 범위 사이에서 일부 우연한 GCF를 통과하지 않습니다). 결과 제한 값이 계산되고 히스토그램이 생성됩니다. "통과"하려면 모든 입력이 히스토그램에 반영되어야하며 (우리가 어떤 것도 "잃어 버리지 않도록하기위한 정신"), 히스토그램에서 두 버킷의 차이는 2보다 클 수 없습니다 (실제로 <= 1이어야 함) , 계속 지켜봐 주시기 바랍니다). 이기는 알고리즘이있는 경우 프로덕션 코드에 직접 잘라 붙여 넣어 회귀를위한 영구 테스트를 수행 할 수 있습니다.

코드는 다음과 같습니다.

    private void TestConstraintAlgorithm(int min, int max, Func<byte[], long, long, long> constraintAlgorithm)
    {
        var histogram = new int[max-min+1];
        for (int i = 1; i <= 10000000; i++)
        {
            //This is the stand-in for the PRNG; produces a known byte array
            var buffer = BitConverter.GetBytes((long)i);

            long result = constraintAlgorithm(buffer, min, max);

            histogram[result - min]++;
        }

        var minCount = -1;
        var maxCount = -1;
        var total = 0;
        for (int i = 0; i < histogram.Length; i++)
        {
            Console.WriteLine("{0}: {1}".FormatWith(i + min, histogram[i]));
            if (minCount == -1 || minCount > histogram[i])
                minCount = histogram[i];
            if (maxCount == -1 || maxCount < histogram[i])
                maxCount = histogram[i];
            total += histogram[i];
        }

        Assert.AreEqual(10000000, total);
        Assert.LessOrEqual(maxCount - minCount, 2);
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionMSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByMSBRejection);
    }

    private long ConstrainByMSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length-1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;
        //Apply a bitmask to the value, removing the MSB on each loop until it falls in the range.
        var mask = long.MaxValue;
        while (result > max - min)
        {
            mask >>= 1;
            result &= mask;
        }
        result += min;

        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionLSBRejection()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByLSBRejection);
    }

    private long ConstrainByLSBRejection(byte[] buffer, long min, long max)
    {
        //Strip the sign bit (if any) off the most significant byte, before converting to long
        buffer[buffer.Length - 1] &= 0x7f;
        var orig = BitConverter.ToInt64(buffer, 0);
        var result = orig;

        //Bit-shift the number 1 place to the right until it falls within the range
        while (result > max - min)
            result >>= 1;

        result += min;
        return result;
    }

    [Test, Explicit("sandbox, does not test production code")]
    public void TestRandomizerDistributionModulus()
    {
        TestConstraintAlgorithm(1009, 7919, ConstrainByModulo);
    }

    private long ConstrainByModulo(byte[] buffer, long min, long max)
    {
        buffer[buffer.Length - 1] &= 0x7f;
        var result = BitConverter.ToInt64(buffer, 0);

        //Modulo divide the value by the range to produce a value that falls within it.
        result %= max - min + 1;

        result += min;
        return result;
    }

... 결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

LSB 거부 (범위 내에서 숫자를 비트 시프트)는 설명하기 쉬운 이유로 끔찍했습니다. 최대 값보다 작을 때까지 숫자를 2로 나누면 최대한 빨리 종료되고 사소한 범위가 아닌 경우 결과를 상위 3 분의 1로 편향시킵니다 (히스토그램의 자세한 결과에서 볼 수 있음) ). 이것은 내가 완성한 날짜부터 본 행동입니다. 모든 시간은 매우 구체적인 날 오후에있었습니다.

MSB 거부 (범위 내에있을 때까지 한 번에 하나씩 가장 중요한 비트를 제거)가 더 좋지만, 각 비트마다 매우 큰 숫자를 잘라내므로 고르게 분산되지 않습니다. 당신은 상단과 하단에 숫자를 얻을 가능성이 없으므로 중간 1/3을 향한 치우침을 얻습니다. 이는 임의의 데이터를 종 모양의 곡선으로 "정규화"하려는 사람에게 도움이 될 수 있지만 2 개 이상의 더 작은 임의의 숫자 (주사위 던지기와 비슷한)의 합은 더 자연스러운 곡선을 제공합니다. 내 목적으로는 실패합니다.

이 테스트를 통과 한 유일한 방법은 모듈로 나누기에 의해 제약을받는 것이 었으며, 이는 또한 세 가지 중에서 가장 빠른 것으로 밝혀졌습니다. Modulo는 정의에 따라 사용 가능한 입력을 고려하여 가능한 한 분포를 생성합니다.


2
따라서 궁극적으로 난수 생성기의 출력을보고 무작위인지 결정하는 프로그램을 원하십니까? "5,4,10,31,120,390,2,3,4"에서와 같이 무작위이지만 "49,39,1,10,103,12,4,189"는 아니었다?
psr

아닙니다. 그러나 실제 PRNG와 최종 결과 사이에 편향을 피하는 것이 좋습니다.
KeithS

그런 다음 PRNG를 조롱해도 괜찮습니다. 값을 맹 글링하지 않는다는 것을 테스트하기 위해 실제 임의의 값이 필요하지 않습니다. 당신이 허용 가능한 범위의 너무 작은 부분 집합에 임의의 값을 압박 버그가있는 경우에, 당신은 점점해야 어떤 특정 값의 잘못을.
psr

조합도 테스트해야합니다. 예를 들어, 화요일 오전 11시 점검 이후 목요일 오후 2시, 금요일 오전 10 시가 예상되는 경우 시간당 대략 동일한 예상 검사가있는 것은 아닙니다.
David Thornley 2012

그것은 PRNG 자체에 대한 테스트입니다. 위와 같이 구성된 구속 메커니즘의 테스트는 철저한 비 랜덤 데이터 세트가 제공되므로 항상 그러한 테스트에 실패합니다. 제약 메커니즘이 임의의 데이터를 "순서화"하려고 노력하지 않는다고 가정하면 단위 테스트에서 수행해서는 안되는 "외부 테스트"라고 부릅니다.
KeithS

답변:


17

여기서 실제로 테스트하고 싶은 것은 랜덤 라이저의 특정 결과 세트가 주어지면 나머지 메소드가 올바르게 수행된다는 것입니다.

그것이 당신이 찾고있는 것이라면 무작위 테스트 를 모의 하여 테스트 영역 내에서 결정적으로 만듭니다.

나는 일반적으로 GUID 생성기 및 DateTime.Now를 포함하여 모든 종류의 비 결정적이거나 예측할 수없는 (테스트를 작성할 때) 데이터에 대한 모의 객체를 가지고 있습니다.

의견에서 편집 : 가능한 가장 낮은 수준에서 PRNG (어제 밤 탈출 한 용어)를 조롱해야합니다. 바이트 배열을 생성 할 때 Int64로 변환 한 후가 아닙니다. 또는 두 수준 모두에서 Int64 배열로의 변환을 의도 한대로 테스트 한 다음 DateTime 배열로의 변환이 의도 한대로 작동하는지 별도로 테스트 할 수 있습니다. Jonathon이 말했듯이 세트 시드를 제공하여이를 수행하거나 반환 할 바이트 배열을 제공 할 수 있습니다.

PRNG의 프레임 워크 구현이 변경되면 중단되지 않기 때문에 후자를 선호합니다. 그러나 시드를 제공하는 한 가지 장점은 프로덕션에서 의도 한대로 작동하지 않는 사례를 발견하면 전체 어레이가 아니라 복제 할 수 있도록 하나의 번호 만 기록하면된다는 것입니다.

이 모든 것을 말하면, 이유 때문에 Pseudo Random Number Generator 라는 것을 기억해야합니다 . 그 수준에서도 약간의 편견 이있을 수 있습니다 .


1
아니요.이 경우에 테스트하고 싶은 것은 랜덤 화기 자체이며, 랜덤 화기에 의해 생성 된 "랜덤"값은 허용 가능한 범위에 걸쳐 고르지 않은 분포를 향한 편향되지 않은 것처럼 여전히 "랜덤"인 동안 지정된 제한 조건 내에 속한다고 주장합니다. 시간 범위. 특정 날짜 / 시간이 특정 제약 조건을 올바르게 통과하거나 실패하는지 결정적으로 테스트 할 수는 있지만 무작위 문제에 의해 생성 된 날짜는 편향되어 예측 가능하다는 것이 실제로 문제였습니다.
KeithS

내가 생각할 수있는 유일한 것은 무작위 추출기가 많은 날짜를 뱉어 히스토그램을 만든 다음 값이 상대적으로 균등하게 분포되도록 주장하는 것입니다. 실제로 임의의 데이터 세트가 더 큰 세트가 반박 할 것이라는 명백한 편견을 보여줄 수 있기 때문에 이는 엄청나게 무거워 보이지만 결정적인 것은 아닙니다.
KeithS

1
그것은 때때로 예측할 수없는 테스트입니다. 당신은 그것을 원하지 않습니다. 솔직히 말해서 내 요점을 오해 한 것 같아 랜덤 라이저라고 부르는 것 안에 임의의 숫자를 생성하는 코드가 있어야합니다. 이 줄은 내가 무작위 추출기로 지칭하는 것이며, 무작위 추출기 ( "무작위"데이터를 기반으로 한 날짜의 분포)라고 부르는 나머지는 테스트하려는 것입니다. 아니면 뭔가 빠졌습니까?
pdr

랜덤 시퀀스 (상관 관계, 블록 상관 관계, 평균, 표준 편차 등)에 대한 통계 통계는 적절하게 작은 샘플을 수행하는 경우에만 예상 범위를 충족하지 못합니다. 샘플링 세트를 늘리거나 허용 오차 막대를 높이십시오
lurscher

1
"해당 수준에서도 약간의 편견"좋은 PRNG를 사용하면 실제 난수와 구분되는 테스트 (실제적인 계산 범위)를 찾을 수 없습니다. 따라서 실제로 좋은 PRNG는 편견이 없다고 가정 할 수 있습니다.
코드 InChaos 2013

23

이것은 어리석은 대답처럼 들리 겠지만, 내가 전에 본 방식이기 때문에 그것을 버릴 것입니다.

PRNG에서 코드를 분리합니다. 무작위 화 시드를 무작위 화를 사용하는 모든 코드에 전달하십시오. 그런 다음 단일 종자 (또는 여러 종자가 기분을 좋게 할 것)에서 '작동'값을 결정할 수 있습니다. 이를 통해 많은 수의 법률에 의존하지 않고도 코드를 적절하게 테스트 할 수 있습니다.

그것은 미치게 들리지만, 이것은 군대가 그것을하는 방법입니다 (그것 또는 그들은 전혀 무작위가 아닌 '무작위 테이블'을 사용합니다)


정확히 : 만약 당신이 알고리즘의 요소를 테스트 할 수 없다면 그것을 추상화하고 조롱하십시오
Steve Greatrex

결정적 시드 값을 지정하지 않았습니다. 대신 "무작위"요소를 완전히 제거하여 특정 PRNG 알고리즘에 의존 할 필요조차 없었습니다. 나는 고르게 분포 된 숫자의 넓은 범위를 감안할 때 내가 사용했던 알고리즘이 바이어스를 도입하지 않고 더 작은 범위로 제한 할 수 있는지 테스트 할 수있었습니다. PRNG 자체를 개발 한 사람이 PRNG 자체를 적절히 테스트해야합니다 (RNGCryptoServiceProvider를 사용하고 있음).
KeithS

"랜덤 테이블"접근 방식과 관련하여 "가역적"숫자 생성 알고리즘이 포함 된 테스트 구현을 사용할 수도 있습니다. 이를 통해 PRNG를 "되감기"하거나 쿼리하여 마지막 N 출력이 무엇인지 확인할 수 있습니다. 특정 시나리오에서는 훨씬 더 심층적 인 디버깅이 가능합니다.
Darien

이것은 바보가 아닙니다. 구글이 스패너 테스트에서 실패 주입을 종이에 따라 재현하는 데 사용하는 것과 같은 방법입니다. :)
Akshat Mahajan

6

"무작위인가 (충분히)"는 매우 미묘한 질문으로 판명되었습니다. 짧은 대답은 전통적인 단위 테스트는 그것을 자르지 않는다는 것입니다. 다수의 임의의 값을 생성하고 다양한 통계 테스트에 제출해야합니다.

패턴이있을 것입니다. 결국 psuedo-random 숫자 생성기를 사용하고 있습니다. 그러나 어떤 시점에서는 상황이 응용 프로그램에 대해 "충분히 충분"할 것입니다. (단순히 생성자가 충분할 경우, 게임을 결정하는 데 실행하기 어려운 시퀀스가 ​​필요한 곳에서 비교적 간단한 생성기만으로도 게임마다 많은 차이가 있습니다. 결정되고 잘 갖추어 진 공격자에 의해).

Wikipedia 기사 http://en.wikipedia.org/wiki/Randomness_tests 및 후속 링크에 자세한 정보가 있습니다.


평범한 PRNG도 통계 테스트에서 어떤 패턴도 보여주지 않습니다. 좋은 PRNG의 경우 실제 난수와 구별하는 것은 사실상 불가능합니다.
코드 InChaos

4

두 가지 답변이 있습니다.

=== 첫 답변 ===

귀하의 질문 제목을 보자 마자 해결책을 제안했습니다. 내 솔루션은 다른 사람들이 제안한 것과 동일합니다. 난수 생성기를 조롱하는 것입니다. 결국, 나는 좋은 단위 테스트를 작성하기 위해이 트릭을 필요로하는 여러 가지 다른 프로그램을 만들었으며 모든 코딩에서 임의의 숫자에 대한 표준 액세스를 모의 할 수있게되었습니다.

그러나 나는 당신의 질문을 읽었습니다. 그리고 당신이 묘사 한 특정 문제에 대해서는 대답이 아닙니다. 문제는 난수를 사용하는 예측 가능한 프로세스를 만들 필요가 없다는 것이 아니기 때문에 테스트 할 수 있습니다. 오히려 문제는 알고리즘이 RNG의 균일 한 무작위 출력을 알고리즘의 균일 한 범위 내 균일 한 출력으로 매핑했는지 확인하는 것입니다. 기본 RNG가 균일 한 경우 검사 시간이 균등하게 분포됩니다. 문제 제약).

그것은 정말로 어려운 (그러나 상당히 잘 정의 된) 문제입니다. 이것은 흥미로운 문제라는 것을 의미합니다. 즉시 이것을 해결하는 방법에 대한 정말 좋은 아이디어를 생각하기 시작했습니다. 내가 핫샷 프로그래머 였을 때이 아이디어로 무언가를 시작했을 수도 있습니다. 그러나 저는 더 이상 핫샷 프로그래머가 아닙니다. 저는 지금 더 경험이 많고 숙련 된 것을 좋아합니다.

그래서 어려운 문제에 빠지지 않고 스스로 생각했습니다. 이것의 가치는 무엇입니까? 답은 실망 스러웠습니다. 버그가 이미 해결되었으며 앞으로이 문제에 대해 부지런히 노력할 것입니다. 외부 환경은 문제를 유발할 수 없으며 알고리즘 만 변경합니다. 이 흥미로운 문제를 해결 한 유일한 이유는 TDD (Test Driven Design)의 관행을 만족시키기 위해서였습니다. 내가 배운 한 가지가 있다면 그것이 가치가 없을 때 어떤 맹목적으로 맹목적으로 준수 하는 것이 문제를 일으키는 것입니다. 내 제안은 이것입니다 : 이것에 대한 테스트를 작성하지 말고 계속하십시오.


=== 두 번째 답변 ===

와우 ... 정말 멋진 문제입니다!

여기서 수행해야 할 것은 검사 날짜 및 시간을 선택하는 알고리즘이 사용하는 RNG가 균일하게 분포 된 숫자를 생성하는 경우 (문제 제약 조건 내에서) 균일하게 분포 된 출력을 생성하는지 검증하는 테스트를 작성하는 것입니다. 난이도별로 정렬 된 몇 가지 방법이 있습니다.

  1. 무차별 대입 할 수 있습니다. 실제 RNG를 입력으로 사용하여 알고리즘을 한 번만 실행하십시오. 출력 결과를 검사하여 균일하게 분포되어 있는지 확인하십시오. 분포가 특정 임계 값 이상으로 완벽하게 균일하지 않고 변화가 있고 임계 값을 너무 낮게 설정할 수없는 문제를 잡으려면 테스트에 실패해야합니다. 즉, 오 탐지 확률 (임의의 확률에 의한 테스트 실패)이 매우 작은 지 (중간 규모 코드 기반의 경우 <1 % 미만, 더 적은 경우)를 보장하기 위해서는 엄청난 수의 실행이 필요합니다. 큰 코드 기반).

  2. 모든 RNG 출력을 입력으로 연결 한 다음 검사 시간을 출력으로 생성하는 함수로 알고리즘을 고려하십시오. 이 기능이 부분적으로 연속적이라는 것을 알고 있다면 속성을 테스트하는 방법이 있습니다. RNG를 모형 RNG로 교체하고 알고리즘을 여러 번 실행하여 균일하게 분산 된 RNG 출력을 생성하십시오. 따라서 코드에서 각각 [0..1] 범위에있는 2 개의 RNG 호출이 필요한 경우 [(0.0,0.0), (0.0,0.1), (0.0, 0.2), ... (0.0,0.9), (0.1,0.0), (0.1,0.1), ... (0.9,0.9)]. 그런 다음 100 회 실행의 출력이 허용 된 범위 내에서 (대략) 균일하게 분포되어 있는지 확인할 수 있습니다.

  3. 알고리즘을 신뢰할 수있는 방식으로 검증해야하고 알고리즘에 대한 가정을하거나 여러 번 실행할 수없는 경우 여전히 문제를 공격 할 수 있지만 알고리즘을 프로그래밍하는 방법에 대한 제약이 필요할 수 있습니다. . 확인 PyPy오브젝트 공간의 예와 같은 접근 방식을. 실제로 알고리즘을 실행 하는 대신 RNG 입력이 균일하다고 가정 할 때 출력 분포의 모양을 계산 하는 오브젝트 공간을 작성할 수 있습니다 . 물론이를 위해서는 이러한 도구를 빌드하고 알고리즘을 PyPy 또는 컴파일러를 대폭 수정하고 코드를 분석하는 데 사용하기 쉬운 다른 도구로 빌드해야합니다.


3

단위 테스트의 경우 랜덤 생성기 를 모든 코너 케이스에 대해 예측 가능한 결과를 생성하는 클래스로 교체하십시오 . 즉, 의사 난 수화기가 가능한 가장 낮은 값과 가장 높은 가능한 값을 생성하고 동일한 결과를 여러 번 연속으로 생성하는지 확인하십시오.

예를 들어 Random.nextInt (1000)이 0 또는 999를 반환 할 때 발생하는 개별 버그를 간과하기를 원하지 않습니다.


3

Sevcikova et al : "Stochastic Systems의 자동 테스트 : 통계적으로 접지 된 접근"( PDF )을 살펴볼 수 있습니다 .

이 방법론은 UrbanSim 시뮬레이션 플랫폼 의 다양한 테스트 사례에서 구현됩니다 .


좋은 것입니다.
KeithS

2

간단한 히스토그램 접근법은 좋은 첫 단계이지만 무작위성을 증명하기에는 충분하지 않습니다. 균일 한 PRNG의 경우 (최소한) 2 차원 산점도를 생성합니다 (여기서 x는 이전 값이고 y는 새로운 값). 이 플롯도 균일해야합니다. 시스템에 의도적 인 비선형 성이 있기 때문에 상황이 복잡합니다.

내 접근 방식은 다음과 같습니다.

  1. 소스 PRNG가 충분히 무작위임을 검증 (또는 주어진 것으로 간주) (표준 통계 측정 사용)
  2. 제한되지 않은 PRNG-to-time-time 변환이 출력 공간에서 충분히 임의적인지 확인합니다 (이로 인해 변환에 바이어스가 없는지 확인). 여기서 간단한 1 차 균일 성 테스트로 충분합니다.
  3. 구속 된 케이스가 충분히 균일한지 확인하십시오 (유효 빈에 대한 간단한 1 차 균일 성 테스트).

이러한 각 테스트는 통계적이며 높은 신뢰도로 오 탐지 및 오탐을 피하기 위해 많은 수의 샘플 포인트가 필요합니다.

변환 / 제약 알고리즘의 특성에 대해 :

주어진 경우 : 의사 난수 값 p 를 생성하는 방법 여기서 0 <= p <= M

필요 : 0 (= 불연속) 범위의 출력 y 0 <= y <= N <= M

연산:

  1. 계산 r = floor(M / N), 즉 입력 범위에 맞는 전체 출력 범위의 수입니다.
  2. p에 허용되는 최대 값을 계산하십시오 .p_max = r * N
  3. 보다 작거나 같은 값을 찾을 때까지 p에 대한 값을 생성p_max
  4. 계산하다 y = p / r
  5. 만약 Y가 수용, 반환 달리 반복 단계 3

열쇠는 불균일하게 접는 대신 허용 할 수없는 값을 버리는 것입니다.

의사 코드에서 :

# assume prng generates non-negative values
def randomInRange(min, max, prng):
    range = max - min
    factor = prng.max / range

    do:
        value = prng()
    while value > range * factor
    return (value / factor) + min

def constrainedRandom(constraint, prng):
    do:
        value = randomInRange(constraint.min, constraint.max, prng)
    while not constraint.is_acceptable(value)

1

코드가 실패하지 않았는지 또는 올바른 장소에서 올바른 예외를 처리하는지 확인하는 것 외에도 유효한 입력 / 응답 쌍 (이를 수동으로 계산)을 생성하고 테스트에서 입력을 공급하고 예상 응답을 반환하는지 확인할 수 있습니다. 좋지는 않지만, 그것은 당신이 할 수있는 거의 전부입니다. 그러나 귀하의 경우에는 일정이 무작위가 아닙니다. 일정을 만든 후에는 규칙 적합성을 테스트 할 수 있습니다. 매주 9-9 사이에 3 번의 검사가 필요합니다. 검사가 수행 된 정확한 시간을 테스트 할 필요가 없습니다.


1

여러 번 실행하고 원하는 배포판을 얻는 지 확인하는 것보다 더 좋은 방법은 없습니다. 50 개의 잠재적 인 검사 일정이있는 경우 테스트를 500 번 실행하고 각 일정을 10 회 가까이 사용하십시오. 랜덤 생성기 시드를 제어하여 결정 성을 높일 수 있지만 테스트를 구현 세부 사항에보다 밀접하게 결합 할 수도 있습니다.


그러나 이것이 무작위 일 경우, 때때로 일정이 전혀 사용되지 않을 것입니다. 때로는 일부 일정이 20 회 이상 사용되기도합니다. 각 일정이 "10 번에 가깝게"사용되는지 테스트하려는 방법을 모르지만 여기에서 테스트하는 조건에 관계없이 프로그램이 사양에 맞게 작동 할 때 실패하는 테스트가 있습니다.
Dawood는 Monica Monica를

표본 크기가 충분하고 (균일성에 대한 합리적인 제한) @DawoodibnKareem을 사용하면 테스트 실패 횟수를 10 억 단위로 줄일 수 있습니다. 그리고 일반적으로 이와 같은 통계는 n과 함께 지수이므로 그 수에 도달하는 데 예상되는 것보다 적게 걸립니다.
mbrig

1

구체적인 정의가없는 성가신 상태는 테스트 할 수 없습니다. 생성 된 날짜가 모든 테스트를 통과하면 이론적으로 응용 프로그램이 올바르게 작동하는 것입니다. 컴퓨터는 그러한 테스트의 기준을 인정할 수 없기 때문에 날짜가 "충분히 임의"인지 알 수 없습니다. 모든 테스트는 통과했지만 응용 프로그램의 동작이 여전히 적합하지 않으면 테스트 범위가 경험적으로 부적절합니다 (TDD 관점에서).

내 관점에서, 최선의 방법은 분포가 인간의 냄새 테스트를 통과하도록 임의의 날짜 생성 제약 조건을 구현하는 것입니다.


2
자동 테스트를 통해 무작위성을 절대적으로 결정할 수 있습니다. 충분히 많은 수의 샘플을 생성하고 표준 무작위 테스트를 적용하여 시스템의 바이어스를 탐지합니다. 이것은 꽤 표준적인 저학년 프로그래밍 연습입니다.
Frank Szczerba

0

랜덤 화기의 출력 (의사, 양자 / 혼돈 또는 현실)을 기록하십시오. 그런 다음 테스트 요구 사항에 맞거나 단위 테스트 사례를 작성할 때 잠재적 인 문제와 버그를 노출시키는 "무작위"시퀀스를 저장하고 재생하십시오.


0

이 사례는 속성 기반 테스트에 이상적입니다 .

간단히 말해 테스트 프레임 워크는 테스트중인 코드에 대한 입력을 생성하고 테스트 어설 션 은 출력의 속성 을 검증하는 테스트 모드입니다 . 그런 다음 프레임 워크는 테스트중인 코드를 "공격"하여 오류가 발생할 수 있도록 충분히 지능적 일 수 있습니다. 프레임 워크는 일반적으로 난수 생성기 시드를 가로 채기에 충분합니다. 일반적으로 최대 N 개의 테스트 사례를 생성하거나 최대 N 초 동안 실행하도록 프레임 워크를 구성하고 마지막 실행에서 실패한 테스트 사례를 기억하고 최신 코드 버전에 대해 먼저 다시 실행하십시오. 이를 통해 개발 중 빠른 반복주기와 대역 외 / CI 내에서 느리고 포괄적 인 테스트를 수행 할 수 있습니다.

다음은 sum함수 를 테스트하는 (멍청하고 실패한) 예입니다 .

@given(lists(floats()))
def test_sum(alist):
    result = sum(alist)
    assert isinstance(result, float)
    assert result > 0
  • 테스트 프레임 워크는 한 번에 하나의 목록을 생성합니다
  • 목록의 내용은 부동 소수점 숫자입니다.
  • sum 호출되고 결과의 속성이 검증됩니다
  • 결과는 항상 부동입니다
  • 결과는 긍정적이다

이 테스트는 많은 "버그"를 찾습니다 sum( 이 모든 것을 스스로 추측 할 수 있다면 주석 ) :

  • sum([]) is 0 (int, float가 아님)
  • sum([-0.9]) 부정적이다
  • sum([0.0]) 엄격하게 긍정적이지 않다
  • sum([..., nan]) is nan 긍정적이지 않은

기본 설정을 사용하면 hpythesis1 개의 "나쁜"입력이 발견되면 테스트가 중단됩니다. 이는 TDD에 적합합니다. 많은 / 모든 "나쁜"입력을보고하도록 구성 할 수 있다고 생각했지만 지금은 해당 옵션을 찾을 수 없습니다.

OP의 경우, 검증 된 속성은 더 복잡 할 것이다 : 검사 유형 A 존재, 검사 유형 A 주 1 회, 검사 시간 B 항상 12pm, 검사 유형 C 9 ~ 9, [주어진 일정은 1 주일] A, B, C 모두 현재 등

가장 잘 알려진 라이브러리는 QuickCheck for Haskell입니다. 다른 언어로 된 라이브러리 목록은 아래 Wikipedia 페이지를 참조하십시오.

https://ko.wikipedia.org/wiki/QuickCheck

가설 (Python의 경우)은 이러한 종류의 테스트에 대한 훌륭한 글을 작성했습니다.

https://hypothesis.works/articles/what-is-property-based-testing/


-1

단위 테스트 할 수있는 것은 임의의 날짜가 유효한지 또는 다른 임의의 날짜를 선택 해야하는지 논리를 결정하는 것입니다.

임의의 날짜를 가져 와서 적절하게 무작위로 결정하는 것보다 짧은 임의 날짜 생성기를 테스트하는 방법은 없습니다.


-1

목표는 단위 테스트를 작성하여 통과하는 것이 아니라 프로그램이 요구 사항에 맞는지 확인하는 것입니다. 이를 수행 할 수있는 유일한 방법은 먼저 요구 사항을 정확하게 정의하는 것입니다. 예를 들어, "임의 시간에 3 주마다 검사"를 언급했습니다. 요구 사항은 다음과 같습니다. (a) 3 번의 검사 (2 또는 4가 아님), (b) 예상치 못한 검사를 원하지 않는 사람들이 예측할 수없는 시간, (c) 너무 가까이 있지 않은- 5 분 간격으로 두 번의 검사를 수행하면 아무 의미가없는 것일 수 있습니다.

그래서 당신은 내가 한 것보다 더 정확하게 요구 사항을 기록합니다. (a)와 (c)는 쉽다. (b)의 경우, 다음 검사를 예측하고 단위 테스트를 통과하려고 할 수있는 한 영리한 코드를 작성하여 해당 코드가 순수한 추측보다 더 잘 예측할 수 없어야합니다.

물론 검사가 실제로 무작위 인 경우 모든 예측 알고리즘 순전히 정확할 있으므로 이러한 상황이 발생하면 자신과 단위 테스트가 당황하지 않도록해야합니다. 몇 가지 테스트를 더 수행하십시오. 난수 생성기를 테스트하는 것을 귀찮게하지 않을 것입니다. 결국 검사 일정이기 때문에 계산 방법이 중요하지 않기 때문입니다.


아뇨 단위 테스트는 프로그램이 요구 사항을 충족하는지 확인하므로 두 가지가 동일합니다. 그리고 난 랜덤 알고리즘을 리버스 엔지니어링하기 위해 예측 소프트웨어를 작성하는 사업에 있지 않습니다. 내가 당신에게 그것에 대해 말하고 싶지 않다면, 나는 열쇠를 예측하고 가장 높은 입찰자에게 비밀을 팔아서 살해하는 안전한 웹 사이트를 만들 것입니다. 내 사업은 제약 조건 내에서 제한적이지만 예측할 수없는 시간을 만드는 스케줄러를 작성하고 있으며, 확실하다고 말하는 확률이 아닌 결정적 테스트가 필요하다는 것을 입증하기 위해 결정적 테스트가 필요합니다.
KeithS
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.