2016-10-29 3 views
0

, поэтому мне трудно понять, почему мой тест в JUnit терпит неудачу. У меня есть класс Bill, класс Money и класс Date. Новый объект Bill создается в тесте и линииКак исправить протекающие методы доступа?

assertTrue(myBill.getAmount().getCents() == 0); 

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

return new Date(dueDate); 

вместо просто

return dueDate; 

, но он по-прежнему неудачу в JUnit. Пожалуйста помоги!

код теста:

@Test 
public void testBillConstructorPrivacyLeak() 
{ 
    Date date1 = new Date(1, 1, 2020); 
    Money money1 = new Money(10); 
    Bill myBill = new Bill(money1, date1, "sam"); 

    date1.setYear(2021); 
    money1.setMoney(5, 10); 

    //Now get values and make sure they have not changed 
    assertTrue(myBill.getAmount().getCents() == 0); 
    assertTrue(myBill.getDueDate().getYear() == 2020); 
} 

Мои классы:

public class Bill 
{ 
private Money amount; 
private Date dueDate; 
private Date paidDate; 
private String originator; 

//paidDate set to null 
public Bill (Money amount, Date dueDate, String originator) { 
    this.amount = amount; 
    this.dueDate = dueDate; 
    this.originator = originator; 
    paidDate = null; 
} 

//copy constructor 
public Bill (Bill toCopy) { 
    this.amount = toCopy.amount; 
    this.dueDate = toCopy.dueDate; 
    this.paidDate = toCopy.paidDate; 
    this.originator = toCopy.originator; 
} 

public Money getAmount() { 
    return new Money(amount); 
} 

public Date getDueDate() { 
    return new Date(dueDate); 
} 

public String getOriginator() { 
    return originator; 
} 

//returns true if bill is paid, else false 
public boolean isPaid() { 
    return (paidDate != null); 
} 

//if datePaid is after the dueDate, the call does not update anything and returns false. 
//Else updates the paidDate and returns true 
//If already paid, we will attempt to change the paid date. 
public boolean setPaid (Date datePaid) { 
    if (datePaid.isAfter(dueDate)) { 
     return false; 
    } 
    else { 
     paidDate = new Date(datePaid); 
     return true; 
    } 
} 

//Resets the due date – If the bill is already paid, this call fails and returns false. 
//Else it resets the due date and returns true. 
public boolean setDueDate (Date newDueDate) { 
    if (isPaid()) { 
     return false; 
    } 
    else { 
     dueDate = new Date(newDueDate); 
     return true; 
    } 
} 

//Change the amount owed. 
//If already paid returns false and does not change the amount owed else changes 
//the amount and returns true. 
public boolean setAmount (Money amount) { 
    if (isPaid()) { 
     return false; 
    } 
    else { 
     amount = new Money(amount); 
     return true; 
    } 
} 

public void setOriginator (String originator) { 
    this.originator = originator; 
} 

//Build a string that reports the amount, when due, to whom, if paid, and if paid 
//the date paid 
public String toString() { 
    return "Amount: " + amount + " Due date: " + dueDate + " To: " + "originator" + " Paid?" + isPaid() + "Paid date: " + paidDate; 
} 

//Equality is defined as each field having the same value. 
public boolean equals (Object toCompare) { 
    if (toCompare instanceof Bill) { 
     Bill that = (Bill) toCompare; 
     return this.amount.equals(that.amount) && 
       this.dueDate.equals(that.dueDate) && 
       this.paidDate.equals(that.paidDate) && 
       this.originator.equals(that.originator); 
    } 
    return false; 
} 

}

public class Money 
{ 
private int dollars; 
private int cents; 

//Constructor which sets the dollar amount, and sets cents to 0 
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException 
public Money (int dol) { 
    if (dol < 0) { 
     throw new IllegalArgumentException ("Must be greater than 0."); 
    } 
    this.dollars = dol; 
    cents = 0; 
} 

//Constructor which initialized dollars and cents. 
//If the user enters in an amount LT 0, you will throw an IllegalArgumentException 
public Money (int dol, int cent) { 
    if (dol < 0 || cent < 0) { 
     throw new IllegalArgumentException ("Must be greater than 0."); 
    } 
    this.dollars = dol; 
    this.dollars += cent/100; 
    this.cents = cent % 100; 
} 

//Copy constructor 
public Money (Money other) { 
    this.dollars = other.dollars; 
    this.cents = other.cents; 
} 

public int getDollars() { 
    return dollars; 
} 

public int getCents() { 
    return cents; 
} 

//If the user enters in an amount LT 0, you will throw an IllegalArgumentException 
public void setMoney (int dollars, int cents) { 
    if (dollars < 0 || cents < 0) { 
     throw new IllegalArgumentException ("Must be greater than 0."); 
    } 
    this.dollars = dollars; 
    this.dollars += cents/100; 
    this.cents = cents % 100; 
} 

//Gets the money amount as a double 
//For example it might return 5.75 
public double getMoney() { 
    return dollars + (cents/100.0); 
} 

//If the user enters in an amount LT 0, you will throw an IllegalArgumentException4 
public void add (int dollars) { 
    if (dollars < 0) { 
     throw new IllegalArgumentException ("Must be greater than 0."); 
    } 
    this.dollars += dollars; 
} 

//If the user enters in an amount LT 0, you will throw an IllegalArgumentException 
public void add (int dollars, int cents) { 
    if (dollars < 0 || cents < 0) { 
     throw new IllegalArgumentException ("Must be greater than 0."); 
    } 
    this.dollars += dollars; 
    this.cents += cents; 
    this.dollars += this.cents/100; 
    this.cents = this.cents % 100; 
} 

//Adds the amounts in other to our money object – reducing cents appropriately. 
public void add (Money other) { 
    this.dollars += other.dollars; 
    this.cents += other.cents; 
    this.dollars += this.cents/100; 
    this.cents = this.cents % 100; 
} 

//Two money objects are the same if they have the same value for dollars and cents. 
public boolean equals (Object o) { 
    if(o instanceof Money) { 
     return this.dollars == ((Money)o).dollars && this.cents == ((Money)o).cents; 
    } 
    return false; 
} 

//Prints out the amount as a string IE “$3.75” or “$4.00” Note the number of digits displayed for cents. 
//Again for testing and grading purposes use this EXACT output format 
public String toString() { 
    String c = String.format("%.02d",cents); 
    return "$" + dollars + "." + c; 
} 

}

+0

Что тест действительно проверяет? Возвращает ли ваш код неправильное значение? Вы прошли через код в отладчике, чтобы узнать, откуда взялось неправильное значение? –

+0

Он проверяет, изменились ли значения (чего они не должны). Но в тесте он присваивает ему новые значения. Значение центов должно быть 0, потому что оно установлено в 0 в конструкторе класса Money. Но в тесте он устанавливает 10 и, следовательно, не работает. Я просто не знаю, как это исправить. –

+0

И не очень хорошо знакомы с использованием отладчика. Это происходит только потому, что в JUnit он настроен на что-то еще. В классе нет ничего плохого. Пока он не будет изменен из внешнего источника. –

ответ

2

Ваша проблема обусловлена ​​тем, что в конструкторе для Bill йо u магазин ссылкиMoney и Date объектов. Затем, когда вы изменяете эти объекты в тестовом примере, вы изменяете одни и те же объекты.

Если вы не хотите, что поведение, которое вы должны сделать глубокую копию из Money и Date объектов в Bill конструктора, то есть:

public Bill (Money amount, Date dueDate, String originator) { 
    this.amount = new Money(amount); 
    this.dueDate = new Date(dueDate); 
    this.originator = originator; 
    paidDate = null; 
} 

Вы не должны делать это для originator потому что строки неизменяемы.

+0

Я попытался изменить «this.amount = toCopy.amount» на «this.amount = new Money (toCopy.amount)», и это не сработало ... есть ли что-то еще, что мне нужно делать? –

+0

Вы смотрите на неправильный конструктор ... но да, что конструктор должен будет иметь такое же изменение и к нему. См. Мой обновленный ответ. –

+0

О, никогда, я просто делал это в конструкторе копирования, а не в исходном конструкторе. Спасибо!!!!! –

1

Хотя вы не показываете реализацию вашего Money класса, тот факт, что у него есть метод setMoney предполагает, что это изменчивое. В этом случае ваша проблема заключается в том, что конструктор Bill не копирует объекты, в которые он был передан, и поэтому любые изменения в money1 также изменяют состояние myBill. Аналогичные замечания относятся к объектам Date.

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

public Bill (Money amount, Date dueDate, String originator) { 
    this.amount = new Money(amount); // needs copy-constructor for Money 
    this.dueDate = new Date(dueDate); // likewise for Date 
    this.originator = originator;  // no copying needed as String is immutable 
    paidDate = null; 
} 

//copy constructor 
public Bill (Bill toCopy) { 
    // Make copies also in the copy-constructor 
    this.amount = new Money(toCopy.amount); 
    this.dueDate = new Date(toCopy.dueDate); 
    this.paidDate = (toCopy.paidDate == null) ? null : new Date(toCopy.paidDate); 
    this.originator = toCopy.originator; 
} 

В общем, проектирование ваши объекты быть изменяемым означает, что вы должны скопировать защищаясь в конструкторах и в других местах.

С другой стороны, проектирование ваших объектов, которые будут неизменными, лучше, поскольку оно позволяет избежать таких проблем (и на самом деле это совет Джошуа Блоха в его книге «Эффективная Ява»), но оказывается, что Java не " Ты тоже много поможешь им, и вполне вероятно, что ты будешь бороться в течение некоторого времени, чтобы закончить их.

Моя рекомендация для вас изучить библиотеку http://immutables.github.io/ для лучшей отправной точки с помощью этого подхода к дизайну.

0

Когда я пытаюсь скопировать код, я получаю сообщение об ошибке в этой строке:

public Date getDueDate() { 
return new Date(dueDate); 
} 

Можете ли вы сказать, что дата конструктор вы используете. Как java.util.date не имеет такого конструктора, который принимает Date как аргументы. Просьба уточнить, чтобы я мог продолжить отладку и ответить на ваш запрос.

Спасибо.

+0

Я действительно понял это уже! Но это связано с тем, что я создал собственный класс Date (часть требования к назначению) вместо использования Java. –

Смежные вопросы