Тестирование устройства для функции isPrime

Я решил, что хочу взять удар по тестированию первого программирования. Итак, прежде чем я начал писать функцию isPrime, я написал этот модульный тест. Это мой первый, и я не уверен, что я делаю это правильно.

Я думал, что мне может понадобиться извлечь циклы только для двух методов, которым я бы передал массив. Один для Assert.IsTrue и один для Assert.IsFalse, но я не был уверен, что это хорошая идея в модульном тесте.

  • Я покрываю свои базы здесь?
  • Какие еще случаи я пропущу?
  • Что бы вы сделали по-другому?
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Challenges;
using System.Numerics;

namespace ChallengesTest
{
    [TestClass]
    public class PrimeTest
    {
        [TestMethod]
        public void SmallPrimes()
        {
            int[] numbers = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };

            for (var i = 0; i < numbers.Length; i++)
            {
                Assert.IsTrue(Numbers.isPrime(numbers[i]));
            }

        }

        [TestMethod]
        public void Negatives()
        {
            int[] numbers = { -2, -3, -5, -7, -11, -13, -17, -19, -23, -29 };

            for (var i = 0; i < numbers.Length; i++)
            {
                Assert.IsFalse(Numbers.isPrime(numbers[i]));
            }
        }

        [TestMethod]
        public void PositiveNotPrime()
        {
            int[] numbers = { 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25 };

            for (var i = 0; i < numbers.Length; i++)
            {
                Assert.IsFalse(Numbers.isPrime(numbers[i]));
            }
        }

        [TestMethod]
        public void ZeroAndOne()
        {
            Assert.IsFalse(Numbers.isPrime(0));
            Assert.IsFalse(Numbers.isPrime(1));
        }

        [TestMethod]
        public void BigPrimes()
        {
            int[] numbers = {104677,104681, 104683, 104693, 104701, 104707, 104711, 104717, 104723, 104729 };

            for (var i = 0; i < numbers.Length; i++)
            {
                Assert.IsTrue(Numbers.isPrime(numbers[i]));
            }
        }
    }
}
12 голосов | спросил RubberDuck 28 J000000Monday14 2014, 03:00:56

4 ответа


5

Это довольно обширный набор тестов, так как каждый из номеров в списке является тестом для сортировки.

Вот несколько советов:

  1. Как насчет тестирования негативов, которые не являются простыми?
  2. Добавить набор тестов для больших простых чисел (идеально в идеале)
  3. Добавить тесты для значений, близких к пределу ints

Наконец, это необязательно, но вы можете добавить некоторые, которые генерируются случайным образом, поэтому вы не можете обманывать и жестко кодировать результаты.

ответил mleyfman 28 J000000Monday14 2014, 03:30:39
10

Методы в C # записываются в PascalCase, поэтому давайте изменим isPrime на IsPrime.

Вы можете использовать LINQ, чтобы сделать код более понятным. Вместо

for (var i = 0; i < numbers.Length; i++)
{
    Assert.IsTrue(Numbers.isPrime(numbers[i]));
}

Вы можете написать

Assert.IsTrue(numbers.All(Numbers.IsPrime));

Вместо

for (var i = 0; i < numbers.Length; i++)
{
    Assert.IsFalse(Numbers.isPrime(numbers[i]));
}

Вы можете написать

Assert.IsFalse(numbers.Any(Numbers.IsPrime));

или

Assert.IsTrue(numbers.All(n => !IsPrime(n)));

в зависимости от того, что вам легче читать (я предпочитаю первое).

На самом деле, я был бы немного ленив и скопировал бы большой список простых чисел откуда-нибудь, а затем напишу

var primes = Enumerable.Range(0, 1000000).Where(Numbers.IsPrime);
Assert.IsTrue(primes.SequenceEqual(PrimesLessThanOneMillion));

Если вы примете предложение @ mleyfman для тестирования со случайно генерируемыми штрихами, обязательно запустите генератор случайных чисел . Модульные тесты должны быть воспроизводимыми.

То есть никогда не делайте этого

var random = new Random();

, но сделайте это

var random = new Random(42);

Одним из преимуществ модульных тестов является то, что если они терпят неудачу, вы знаете, что это связано с изменением кода и может затем использовать bisect (или подобное) для обнаружения прерывания. Если единичный тест завершился неудачей из-за чего-то, что не является изменением кода (например, другого семестра RNG), вы потеряли это важное преимущество.

Изменить: как отметил @svick, использование одного и того же семпла может не генерировать одну и ту же последовательность между различными версиями или реализациями .NET. См. эти ответы на StackOverflow. Если все dev-машины (включая сервер сборки) работают с одной и той же версией .NET, вы должны быть в порядке.

ответил mjolka 28 J000000Monday14 2014, 03:42:21
3

Нейминг

Чтобы понять, почему тест не удается, мы должны уметь понять, что тест должен тестировать, просто взглянув на имя метода.

Итак, посмотрим на ваш тестовый метод

public void SmallPrimes()

не скажет вам, в случае неудачи, что этот тестовый метод должен тестировать.

Единичные методы тестирования следует называть следующими MethodName_StateUnderTest_ExpectedBehavior.

Итак, лучшее имя будет:

public void IsPrime_RangeOfPrimesFromTwoToTwentyNine_Evaluated() 

и

public void Negatives()

станет

public void IsPrime_RangeOfNegativeNumbers_Evaluated()

Для

public void ZeroAndOne()

Я вижу другую проблему. Имя метода подразумевает, что этот метод имеет две обязанности. Если вы не возражаете, вы должны по крайней мере предоставить сообщение Assert, чтобы указать, какая часть не выполнена.

Итак, изменив этот метод на

[TestMethod]
public void IsPrime_NumbersZeroAndOne_Evaluated()
{
    Assert.IsFalse(Numbers.isPrime(0),"Failed for {0}",0);
    Assert.IsFalse(Numbers.isPrime(1),"Failed for {0}",1);
}

покажет вам, если этот тест завершится неудачно, с каким номером у вашего кода возникают проблемы.

или лучше

[TestMethod]
public void IsPrime_NumberZero_Evaluated()
{
    Assert.IsFalse(Numbers.isPrime(0), "Failed for {0}",0);
}

[TestMethod]
public void IsPrime_NumberOne_Evaluated()
{
    Assert.IsFalse(Numbers.isPrime(1), "Failed for {0}",1);
}

предыдущий SmallPrimes() testmethod станет

 [TestMethod]
 public void IsPrime_RangeOfPrimesFromTwoToTwentyNine_Evaluated()()
 {
     int[] numbers = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };

     for (var i = 0; i < numbers.Length; i++)
     {
         Assert.IsTrue(Numbers.isPrime(numbers[i]), "Failed for {0}",numbers[i]);
     }

 }

Некоторые могут жаловаться на то, что имена методов слишком длинные, но в конечном итоге все это связано с удобочитаемостью.

ответил Heslacher 28 J000000Monday14 2014, 14:29:41
2
  

Чем бы вы поступили иначе?

Я бы использовал NUnit, а не инструменты тестирования Microsoft. NUnit имеет лучшую библиотеку утверждений (например, Assert.Throws<T>()) весьма полезен). Он также имеет более легкое тестирование, основанное на данных, которое позволит вам удалить всю логику цикла из вашего кода. Например:

[Test]
[TestCase(2)]
[TestCase(3)]
[TestCase(5)]
[TestCase(7)]
[TestCase(11)]
[TestCase(13)]
[TestCase(17)]
[TestCase(19)]
[TestCase(23)]
[TestCase(29)]
public void SmallPrimes(int num)
{
    Assert.IsTrue(Numbers.isPrime(num));
}

По общему признанию, это много строк, но их очень легко понять. Кроме того, сам метод тестирования становится мертвым простым.

Так как я нахожусь в теме инструментария, возможно, вы захотите заглянуть в плагин TestDriven.NET Visual Studio, который обеспечивает очень приятную интеграцию «щелчок правой кнопкой мыши -> запуск теста».

ответил breischl 31 J000000Thursday14 2014, 22:27:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132