Jak testować kod, czy nie wpada w pętlę nieskończoną?

0

Testujemy metodę, która jest źle napisana i wpada w pętlę nieskończoną. Jak zrobić, by taki test padł? Domyślnie test się po prostu pętli w nieskończoność i już.

W VisualStudio jest jeszcze jako tako, bo widać, które testy trwają za długo, więc można ręcznie testy ubić i przyjrzeć się tym, które się nie skończyły.
Ale w VS Code (przynajmniej domyślnie) pętlące się testy są nieużywalne. Ubicie testowania powoduje, że nie widać outputu, nie wiadomo, które testy poszły źle.

Przykład:

public static class Class1
{
    public static int CountTrailingZeros(this byte b)
    {
        int ret = 0;
        for (; b % 2 == 0; b /= 2)
            ret++;
        return ret;
    }
}

public class UnitTest1
{
    [Theory]
    [InlineData((byte)4, 2)]
    [InlineData((byte)0, 8)]
    public void Test1(byte b, int expectedTrailingZerosNo)
    {
        var actualTrailingZerosNo = b.CountTrailingZeros();

        Assert.Equal(expectedTrailingZerosNo, actualTrailingZerosNo);
    }
}

Celowo wprowadziłem pętlę nieskończoną w CountTrailingZeros dla szczególnego przypadku, gdy argument tej funkcji jest równy 0.
Czy dałoby się jakoś zmodyfikować ten test, tak by wywalał się na tym bugu?

3

automatyczny timeout

timed-out == failed

3

@YetAnohterone:
Dla całego testu
[Test, Timeout(2000)]
(https://docs.nunit.org/articles/nunit/writing-tests/attributes/timeout.html)

Ewentualnie dla asercji
Assert.That(actual, Is.EqualTo(expected).After(5000, 50));
(https://stackoverflow.com/questions/4605514/timeout-for-individual-tests-in-nunit)

Warto dać z niewielkim zapasem jakby przytkało wykonanie testu.

0
twoj_stary_pijany napisał(a):

https://pl.wikipedia.org/wiki/Problem_stopu

Przeciez to zupelnie co innego.. W szczegolnosci w problemie stopu chodzi o uniwersalnosc

0

https://pl.wikipedia.org/wiki/Problem_stopu

@twoj_stary_pijany: sugerujesz, żeby każdy test odpalał wcześniej statyczną analizę kodu i odpalał kod tylko jeżeli stwierdzi, że kod nie ma pętli nieskońcoznej? Odważnie i nowatorsko.

0
CosmoFire napisał(a):

@YetAnohterone:
Dla całego testu
[Test, Timeout(2000)]
(https://docs.nunit.org/articles/nunit/writing-tests/attributes/timeout.html)

Ewentualnie dla asercji
Assert.That(actual, Is.EqualTo(expected).After(5000, 50));
(https://stackoverflow.com/questions/4605514/timeout-for-individual-tests-in-nunit)

Warto dać z niewielkim zapasem jakby przytkało wykonanie testu.

To jest NUnit. A ja zacząłem próbować się uczyć testowania na xunit ;/

Podobno są z tym timeoutem w NUnit są jakieś niemiłe niuanse zresztą, np. test pętlący się nie jest ubijany po timeoucie tylko dalej działa w tle razem z innymi testami, aż cały test suite się skończy. No i cleanup nie jest dla pętlącego się testu wołany

Moje szukanie wykazało, że w xunit teoretycznie też jest timeout, ale... nie działa dla kodu synchronicznego, wymaga wyłączenia współbieżności testów, a ja jeszcze zauważyłem, że zdaje się w ogóle nie działać dla Theory, a tylko jako tako działa dla Fact.

Sytuację w xunit opisują issues https://github.com/xunit/xunit/issues/217 oraz https://github.com/xunit/xunit/issues/2222

Ostatecznie zatem jako tako działa to:

[CollectionDefinition("asdf", DisableParallelization=true)]
public class UnitTest1
{
    [Fact(Timeout=20)]
    public async Task Test1()
    {
        await Task.Run(() =>
        {
            var actualTrailingZerosNo = ((byte)0).CountTrailingZeros();

            Assert.Equal(8, actualTrailingZerosNo);

        });
    }
}

Ale cuda z tym związane chyba wykluczają dawanie z defaultu timeoutów dla każdego testu w razie jeśli gdzieś jest pętla nieskończona, tylko raczej trzeba dawać to tylko tam, gdzie problemy już wystąpiły / są jakoś uprawdopodobnione... zarówno w xUnit, jak i w nunit...

0
const int reasonable_limit = 10_000;

if (infinite_loop_detection_enabled && counter > reasonable_limit)
    throw new XdException();

+ możność wyłączenia.

0

Mozna udowodnic formalnie tzw. własność stopu ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1