Какие дополнительные тесты JUnit могли бы улучшить покрытие класса QuaternaryNumber в Java?JAVA

Программисты JAVA общаются здесь
Anonymous
Какие дополнительные тесты JUnit могли бы улучшить покрытие класса QuaternaryNumber в Java?

Сообщение Anonymous »

Я реализовал класс QuaternaryNumber на Java, который представляет числа по основанию 4 (четвертичная система). Класс расширяет Number и поддерживает арифметические операции, такие как add, sub, mul, div и остаток.
У меня уже есть тесты JUnit 5, охватывающие:
  • Конструкторы (long, String, copy)
  • Все арифметические операции с обоими Параметры QuaternaryNumber и длинные
  • Пограничные случаи, такие как деление на ноль и ввод пустой/недопустимой строки
  • Код: Выделить всё

    intValue()
    , longValue(), floatValue(), doubleValue()
  • Код: Выделить всё

    toString()
    , Equals() и hashCode()
Вот моя текущая реализация:

Код: Выделить всё

package pr2.strukturierung.quaternary;

import java.util.Objects;

/**
* A representation of a number in the quaternary system.
*
* "I, For One, Welcome Our New Alien Overlords".
*
* @author Team-02
* @version 2.0
*/

public class QuaternaryNumber extends Number {

//stores String and long values
private final byte[] digits;

/**
* If a negative number is entered, it gets converted by Math.abs(decimal).
* To remember that it was negative, we need to store that information.
*/
private final boolean isNegative;

/**
* long constructor.
* convert long to base 4 and store the number in an array
*
* @param decimal new object number
*/
public QuaternaryNumber(long decimal) {

long decimalValue = decimal;

if (decimalValue < 0) {
isNegative = true;
decimalValue = Math.abs(decimal);
} else {
isNegative = false;
}

//without the if-condition we would have a empty Array
if (decimalValue == 0) {
digits = new byte[] { 0 };
return;
}

int count = 0;
long temporaryValue = decimalValue;

// count how many digits we need for the array
while (temporaryValue > 0) {
count++;
temporaryValue /= 4;
}

digits = new byte[count];
//0 = 1 //1 = 3 //2 = 1
// convert from base 10 to base 4
for (int i = 0; i < count; i++) {
digits[i] = (byte) (decimalValue % 4); //5 % 4 = 1  -> digits[0] = 1
decimalValue /= 4; //5 / 4 = 1  -> decimalValue now 1
}
}

/**
* String constructor.
* checks if a valid number was entered and stores the value in a byte array
*
* @param base4 string input of the base4 number
*/
public QuaternaryNumber(String base4) {

if (base4 == null || base4.isEmpty()) {
throw new IllegalArgumentException("Input must not be empty");
}

String baseValue = base4;

if (baseValue.charAt(0) == '-') {
isNegative = true;
baseValue = baseValue.substring(1);
} else {
isNegative = false;
}

//removes leading zeros
while (baseValue.charAt(0) == '0' && baseValue.length() > 1) {
baseValue = baseValue.substring(1);
}

// if the number is 0, an array is created directly,
//avoiding the creation of an empty array below
if (baseValue.charAt(0) == '0') {
digits = new byte[] { 0 };
return;
}

//checks if only a number in the range of 0 to 3 was entered
for (int i = 0; i < baseValue.length(); i++) {
char c = baseValue.charAt(i);
if (c < '0' || c > '3') {
throw new IllegalArgumentException("Invalid input.\nInput has to be a number");
}
}

digits = new byte[baseValue.length()];

for (int i = 0; i < baseValue.length();  i++) {
//the string input e.g 123 must be read from right to left       //48
digits[i] = (byte) (baseValue.charAt(baseValue.length() - 1 - i) - '0');
}                                //charAt gives us a ASCII-Value, but we want a number
}

/**
* copy constructor for QuaternaryNumber.
* @param other the QuaternaryNumber to copy
*/
public QuaternaryNumber(QuaternaryNumber other) {
this.isNegative = other.isNegative;
byte[] copy = new byte[other.digits.length];

for (int i = 0; i < copy.length; i++) {
copy[i] = other.digits[i];
}

// cant just do this.digits = other.digits, would point to same array
this.digits = copy;
}

/**
* adds two QuaternaryNumber objects.
* @param other the QuaternaryNumber
* @return a new QuaternaryNumber
*/
public QuaternaryNumber add(QuaternaryNumber other) {
long result = this.longValue() + other.longValue();
//object is immutable.  For that reason it has to be a new Object
return new QuaternaryNumber(result);
}

/**
* adds one QuaternaryNumber and a long-value.
* @param other the long value
* @return a new QuaterayNumber
*/
public QuaternaryNumber add(long other) {
return add(new QuaternaryNumber(other));
}

/**
* subs two QuaternaryNumbers objects.
* @param other the QuaternaryNumber
* @return a new QuaternayerNumber
*/
public QuaternaryNumber sub(QuaternaryNumber other) {
long result = this.longValue() - other.longValue();
return new QuaternaryNumber(result);
}

/**
* subtracted one QuaternaryNumber and a long-value.
* @param other the long value
* @return a new QuaternayNumber
*/
public QuaternaryNumber sub(long other) {
return sub(new QuaternaryNumber(other));
}

/**
* multiplies two QuaternaryNumber objects.
* @param other the QuaternaryNumber
* @return a new QuaternaryNumber
*/

public QuaternaryNumber mul(QuaternaryNumber other) {
long result = this.longValue() * other.longValue();
return new QuaternaryNumber(result);
}

/**
* multiplies one QuaternaryNumber object and a long-value.
* @param other the long value
* @return a new QuaternaryNumber
*/
public QuaternaryNumber mul(long other) {
return mul(new QuaternaryNumber(other));
}

/**
* divides two QuaternaryNumber objects.
* @param other the QuaternaryNumber
* @return a new QuaternaryNumber
*/
public QuaternaryNumber div(QuaternaryNumber other) {
if (other.longValue() == 0) {
throw new ArithmeticException("Division by zero.");
}
long result = this.longValue() / other.longValue();
return new QuaternaryNumber(result);
}

/**
* divides one QuaternaryNumber object and a long-value.
* @param other the long value
* @return a new QuaternaryNumber
*/
public QuaternaryNumber div(long other) {
return div(new QuaternaryNumber(other));
}

/**
* calculates the integer division remainder of two QuaternaryNumber objects.
* @param other the QuaternaryNumber
* @return a new QuaternaryNumber as a reminder
*/
public QuaternaryNumber remainder(QuaternaryNumber other) {
if (other.longValue() == 0) {
throw new ArithmeticException("Division by zero.");
}
long result = this.longValue() % other.longValue();
return new QuaternaryNumber(result);
}

/**
* calculates the integer division remainder of one QuaternaryNumber objects and a long-value.
* @param other the long value
* @return a new QuaternaryNumber as a reminder
*/
public QuaternaryNumber remainder(long other) {
return remainder(new QuaternaryNumber(other));
}

@Override
public int intValue() {
return (int) (longValue());
}

@Override
public long longValue() {
long result = 0;
long base = 1;  // 1 because first place value is 4^0 = 1

for (byte b : digits) {
result += b * base;
base *= 4;
}
if (isNegative) {
return -result;
}
return result;
}

@Override
public float floatValue() {
return (float) (longValue());
}

@Override
public double doubleValue() {
return longValue();
}

@Override
public String toString() {
String result = isNegative ? "-" : ""; // prepends a minus sign if the number is negative

// go through the array backwards and add each digit to result
for (int i = digits.length - 1; i >= 0; i--) {
result += digits[i];
}
return result;  // return the final string
}

@Override
public boolean equals(Object obj) {

// check if obj is a QuaternaryNumber, if not return false
if (!(obj instanceof QuaternaryNumber)) {
return false;
}

// cast obj to QuaternaryNumber and compare the long values
QuaternaryNumber other = (QuaternaryNumber) obj;
return this.longValue() == other.longValue();
}

@Override
public int hashCode() {
return Objects.hash(longValue());
}
}
И мой текущий тестовый класс:

Код: Выделить всё

package pr2.strukturierung.quaternary;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;

import static org.junit.jupiter.api.Assertions.*;

/**
* Tests für die Klasse QuaternaryNumber.
*/
class QuaternaryNumberTest {

private QuaternaryNumber zero;
private QuaternaryNumber positive;
private QuaternaryNumber negative;
private QuaternaryNumber numberWithZero;
private QuaternaryNumber copy;

@BeforeEach
void setup() {

this.zero = new QuaternaryNumber(0);
this.positive = new QuaternaryNumber(5);
this.negative = new QuaternaryNumber(-3);
this.numberWithZero = new QuaternaryNumber("03");
this.copy = new QuaternaryNumber(this.positive);
}

@Test
void testLongConstructorWithZero() {
assertEquals(0L, this.zero.longValue());
}

@Test
void testLongConstructorWithPositiveNumber() {
assertEquals(5L, this.positive.longValue());
}

@Test
void testLongConstructorWithNegativeNumber() {
assertEquals(-3L, this.negative.longValue());
}

@Test
void testStringConstructorWithEmptyInput() {
assertThrows(IllegalArgumentException.class, () ->  {new QuaternaryNumber("");});
}

@Test
void testStringConstructorWithWrongInput() {
assertThrows(IllegalArgumentException.class, () -> { new QuaternaryNumber("2m");});
}

@Test
void testStringConstructorWithZeroBeforeANumber() {
assertEquals("3", this.numberWithZero.toString());
}

@Test
void testCopyConstructor() {
assertEquals(5, this.copy.longValue());
}

@Test
void testAddTwoQuaternaryNumber() {
assertEquals(2L,this.positive.add(this.negative).longValue());
}

@Test
void testSubTwoQuaternaryNumber() {
assertEquals(8L, this.positive.sub(this.negative).longValue());
}

@Test
void testMullTwoQuaternaryNumber() {
assertEquals(-15L, this.positive.mul(this.negative).longValue());
}

@Test
void testDivTwoQuaternaryNumber() {
assertEquals(-1L, this.positive.div(this.negative).longValue());
}

@Test
void testRemainderTwoQuaternaryNumberWithZeorNumber() {
assertThrows(ArithmeticException.class, () -> {this.zero.remainder(this.zero);});
}

@Test
void testRemainderTwoQuaternaryWithValidNumber() {
assertEquals(2L, this.positive.remainder(this.negative).longValue());
}

@Test
void testAddOneQuaternaryNumberAndOneLongValue() {
assertEquals(2L, this.positive.add(-3).longValue());
}

@Test
void testSubOneQuaternaryNumberAndOneLongValue() {
assertEquals(8L, this.positive.sub(-3).longValue());
}
@Test
void testMullOneQuaternaryNumberAndOnelOngValue() {
assertEquals(-15L, this.positive.mul(-3).longValue());
}

@Test
void testDivOneQuaternaryNumberAndOneLongValue() {
assertEquals(-1L, this.positive.div(-3).longValue());
}

@Test
void testRemainderOneQuaternaryNumberandOneLongValueWithZero() {
assertThrows(ArithmeticException.class, () ->  {this.zero.remainder(0);});
}

@Test
void testReaminderOneQuaternaryNumberAndOneLongValueWithValidNumber() {
assertEquals(2L, this.positive.remainder(-3).longValue());
}

@Test
void testIntValue() {
assertEquals(5, this.positive.intValue());
}

@Test
void testLongValue() {
assertEquals(5L, this.positive.longValue());
}

@Test
void testFloatValue() {
assertEquals(5.0f, this.positive.floatValue());
}

@Test
void tDoubleValue(){
assertEquals(5.0d, this.positive.doubleValue());
}

@Test
void testEquals() {
assertEquals(this.positive, this.positive);
}

@Test
void testNotEquals() {
assertNotEquals(this.positive, this.negative);
}

@Test
void testHashcode() {
assertEquals(this.positive.hashCode(), this.positive.hashCode());
}

@Test
void testNotHashCode() {
assertNotEquals(this.positive.hashCode(), this.negative.hashCode());
}
@Test
void testToString() {
assertEquals("11", this.positive.toString());
}
}
Какие дополнительные тесты JUnit вы бы порекомендовали, чтобы улучшить тестовое покрытие и выявить потенциальные крайние случаи, которые я мог пропустить?

Вернуться в «JAVA»