C#. Длинная арифметика. Урок 8. Тестирование

Длинная арифметика   21 марта 2013  Автор статьи:  

Мы написали кучу кода, внесли много изменений во все методы, как быть уверенным в том, что у нас все до сих пор работает корректно? Данная статья будет полезна и тем, кто решил разобраться в длинной арифметике и тем, кто просто решил разобраться в тестировании приложений. В ней мы покроем наш класс больших целых чисел тестами, которые будут проверять работоспособность наших методов и после внесения дальнейших изменений. Чуть — чуть расскажу о тестировании приложений. Обычно самые критические области покрываются тестами, чтобы при любых изменениях быть уверенным, что наш код продолжает работать. Это не гарантирует то, что у нас не закралась какая — то хитрая ошибка, но может служить некоторым сигналом, что мы где — то что — то сломали, что до этого прекрасно работало. Так как мы в рамках данного цикла статей по длинной арифметики реализуем операции над числами, то нам конечно важно, чтобы они выполнялись. Естественно имеет смысл тестировать только публичные методы, в нашем случае это стандартные операции и конструктор. Важную роль при тестировании будет выполнять метод Equal:

public override bool Equals(object obj)
{
BigInteger target = (BigInteger)obj;
if (target.arr.Count == arr.Count)
{
for (int i = 0; i < arr.Count; i++) { if (arr[i] != target.arr[i]) { return false; } } } else { return false; } return sign==target.sign; }

Данный метод нужен для сравнения двух BigInt, так как он используется в таких операциях, как AreEqual у класса Assert. Для создания тестов достаточно правой кнопочкой нажать на исходный код класса, который вы собираетесь покрыть тестами и выбрать "Создать модульные тесты". После этого достаточно выбрать методы, которые вы будете тестировать. Дальше я буду рассматривать метод, который мы будем тестировать и выкладывать код тестирующий его.
Конструктор класса. На самом деле тут мало, что можно проверить, так как у нас нет доступа к защищенным членам класса. Но как говорится не больно и хотелось. Проверим, что вообще что - то создается, и создается гарантированно верно:

[TestMethod()]
public void BigIntegerConstructorTest()
{
string s = "1"; // TODO: инициализация подходящего значения
BigInteger target = new BigInteger(s);
Assert.AreEqual(BigInteger.One, target);
}

Теперь проверим наш метод Equal. Для этого достаточно проверить, что он работает с отрицательными и неотрицательными числами, ну и некоторые гарантированные проверки:

[TestMethod()]
public void EqualsTest()
{
BigInteger first = new BigInteger("1");
bool expected = true;
bool actual = first.Equals(BigInteger.One);
Assert.AreEqual(expected, actual,"Сравнение единиц");
Assert.AreEqual(new BigInteger("0"), BigInteger.Zero, "Сравнение нулей");
Assert.AreEqual(new BigInteger("1234567890"), new BigInteger("1234567890"), "1234567890");
Assert.IsFalse(new BigInteger("-1234567890").Equals(new BigInteger("1234567890")), "+!=-");
Assert.AreEqual(new BigInteger("-12345678900987654321"), new BigInteger("-12345678900987654321"), "-12345678900987654321");
}

Теперь попробуем проверить сложение. Для этого нам надо хотя бы рассмотреть 4 случая:

[TestMethod()]
public void AddTest()
{
Assert.AreEqual(new BigInteger("3333333333"), new BigInteger("1111111111").Add(new BigInteger("2222222222")));
Assert.AreEqual(new BigInteger("-3333333333"), new BigInteger("-1111111111").Add(new BigInteger("-2222222222")));
Assert.AreEqual(new BigInteger("1111111111"), new BigInteger("-1111111111").Add(new BigInteger("2222222222")));
Assert.AreEqual(new BigInteger("-1111111111"), new BigInteger("1111111111").Add(new BigInteger("-2222222222")));
}

Тоже самое для вычитания:

[TestMethod()]
public void SubstractTest()
{
Assert.AreEqual(new BigInteger("888888889"), new BigInteger("1000000000").Substract(new BigInteger("111111111")));
Assert.AreEqual(new BigInteger("11111111111"), new BigInteger("10000000000").Substract(new BigInteger("-1111111111")));
Assert.AreEqual(new BigInteger("-11111111111"), new BigInteger("-10000000000").Substract(new BigInteger("1111111111")));
Assert.AreEqual(new BigInteger("-8888888889"), new BigInteger("-10000000000").Substract(new BigInteger("-1111111111")));

}

Теперь напишем тесты для умножения длинного числа на короткое:

[TestMethod()]
public void MultiplyTest()
{
Assert.AreEqual(new BigInteger("1111111111"), new BigInteger("1111111111").Multiply(1));
Assert.AreEqual(new BigInteger("-2469135780"), new BigInteger("1234567890").Multiply(-2));
Assert.AreEqual(new BigInteger("-3703703670"), new BigInteger("-1234567890").Multiply(3));
Assert.AreEqual(new BigInteger("11111111010"), new BigInteger("-1234567890").Multiply(-9));
}

Для того, чтобы проверить правильность работы сравнения необходимо, что он правильно сравнивает числа с нулем, проверить что правильно сравнивает отрицательные числа между собой, положительные числа между собой и положительные с отрицательными:

[TestMethod()]
public void CompareToTest()
{
Assert.AreEqual(-1, new BigInteger("0").CompareTo(BigInteger.One));
Assert.AreEqual(0, new BigInteger("0").CompareTo(BigInteger.Zero));
Assert.AreEqual(1, new BigInteger("0").CompareTo(new BigInteger("-1")));
Assert.AreEqual(-1, new BigInteger("1234567890").CompareTo(new BigInteger("1234567891")));
Assert.AreEqual(-1, new BigInteger("1234567890").CompareTo(new BigInteger("12345678000")));
Assert.AreEqual(0, new BigInteger("1234567890").CompareTo(new BigInteger("1234567890")));
Assert.AreEqual(1, new BigInteger("1234567890").CompareTo(new BigInteger("1134567891")));
Assert.AreEqual(1, new BigInteger("1234567890").CompareTo(new BigInteger("123456789")));
Assert.AreEqual(-1, new BigInteger("-1234567890").CompareTo(new BigInteger("1234567890")));
Assert.AreEqual(1, new BigInteger("1234567890").CompareTo(new BigInteger("-1234567890")));
Assert.AreEqual(1, new BigInteger("-1234567890").CompareTo(new BigInteger("-1234567891")));
Assert.AreEqual(1, new BigInteger("-1234567890").CompareTo(new BigInteger("-12345678000")));
Assert.AreEqual(0, new BigInteger("-1234567890").CompareTo(new BigInteger("-1234567890")));
Assert.AreEqual(-1, new BigInteger("-1234567890").CompareTo(new BigInteger("-1134567891")));
Assert.AreEqual(-1, new BigInteger("-1234567890").CompareTo(new BigInteger("-123456789")));
}

Теперь напишем тесты для длинного умножения:

[TestMethod()]
public void MultiplyTest1()
{
Assert.AreEqual(new BigInteger("1219326311126352690"), new BigInteger("1234567890").Multiply(new BigInteger("987654321")));
Assert.AreEqual(new BigInteger("-100000000000000000000"), new BigInteger("-10000000000").Multiply(new BigInteger("10000000000")));
Assert.AreEqual(new BigInteger("-99999999980000000001"), new BigInteger("9999999999").Multiply(new BigInteger("-9999999999")));
Assert.AreEqual(new BigInteger("1524157876238378411126352690"), new BigInteger("-1234567890987654321").Multiply(new BigInteger("-1234567890")));
}

Проверим правильно происходит преобразование из BigInt в строку:

[TestMethod()]
public void ToStringTest()
{
Assert.AreEqual("1000000000", new BigInteger("1000000000").ToString());
Assert.AreEqual("-1234567890", new BigInteger("-1234567890").ToString());
}

Проверим деление длинного на короткое:

[TestMethod()]
public void DivideTest()
{
int r = 0;
Assert.AreEqual(new BigInteger("5000000000"), new BigInteger("10000000000").Divide(2, out r));
Assert.AreEqual(0, r);
Assert.AreEqual(new BigInteger("-5000000000"), new BigInteger("-10000000001").Divide(2, out r));
Assert.AreEqual(-1, r);
Assert.AreEqual(new BigInteger("-137174210"), new BigInteger("1234567890").Divide(-9, out r));
Assert.AreEqual(0, r);
Assert.AreEqual(new BigInteger("1388888888"), new BigInteger("-11111111111").Divide(-8, out r));
Assert.AreEqual(7, r);
}

Теперь протестируем деление длинного на длинное:

[TestMethod()]
public void DivTest()
{
Assert.AreEqual(new BigInteger("100005499472470"), new BigInteger("1234567890987654321").Div(new BigInteger("12345")));
Assert.AreEqual(new BigInteger("-156470034"), new BigInteger("-1234567890987654321").Div(new BigInteger("7890123456")));
Assert.AreEqual(new BigInteger("-5000000000"), new BigInteger("11111111111111111111").Div(new BigInteger("-2222222222")));
Assert.AreEqual(new BigInteger("1514249642388491"), new BigInteger("-373737373737373737373737").Div(new BigInteger("-246813579")));
Assert.AreEqual(BigInteger.Zero, new BigInteger("100").Div(new BigInteger("101")));
}

Найдем остаток от деления и проверим его корректность:

[TestMethod()]
public void ModTest()
{
Assert.AreEqual(new BigInteger("1"), new BigInteger("1111111111").Mod(new BigInteger("2")));
Assert.AreEqual(new BigInteger("1111111111"), new BigInteger("1111111111").Mod(new BigInteger("2000000000000000")));
Assert.AreEqual(new BigInteger("12171"), new BigInteger("1234567890987654321").Mod(new BigInteger("12345")));
Assert.AreEqual(new BigInteger("-5563136817"), new BigInteger("-1234567890987654321").Mod(new BigInteger("7890123456")));
Assert.AreEqual(new BigInteger("-1111111111"), new BigInteger("11111111111111111111").Mod(new BigInteger("-2222222222")));
Assert.AreEqual(new BigInteger("165254448"), new BigInteger("-373737373737373737373737").Mod(new BigInteger("-246813579")));
}

Осталось проверить возведение в степень:

[TestMethod()]
public void PowTest()
{
Assert.AreEqual(new BigInteger("-8"), new BigInteger("-2").Pow(new BigInteger("3"), new BigInteger("100")));
Assert.AreEqual(new BigInteger("624224137216"), new BigInteger("2").Pow(new BigInteger("1024"), new BigInteger("1000000000000")));
}

Теперь мы покрыли все методы какими - то тестами и более менее можем быть уверенны, что наши методы работают корректно. Можно приступать к дальнейшей их эксплуатации и создавать новые. P.S в ходе написании данной статьи было исправлено много ошибок...

Научиться программировать

  • на Delphi

  • на Java

  • на C++