因此我无法弄清楚为什么我在JUnit中的测试失败了。我有一个Bill
类,一个Money
类和一个Date
类。正在测试和行
Bill
对象
assertTrue( myBill.getAmount().getCents() == 0);
失败了。所以我知道它发生在哪里,但我不确定如何解决它。我试过将mutator方法更改为
之类的东西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 :(得分:2)
您的问题是因为Bill
的构造函数中存储引用到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
执行此操作,因为字符串是不可变的。
答案 1 :(得分: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;
}
一般来说,将对象设计为可变意味着你必须在构造函数和其他地方进行防御性复制。
另一方面,将对象设计为不可变的更好,因为它避免了这些问题(实际上是Joshua Bloch在他的&#34; Effective Java&#34;书中给出的建议),但事实证明Java也不能帮助你很多,而且很可能你会在很长一段时间内完成这些工作。
我建议您使用此设计方法探索http://immutables.github.io/库以获得更好的起点。
答案 2 :(得分:0)
当我尝试复制您的代码时,我在这一行中收到错误:
public Date getDueDate () {
return new Date(dueDate);
}
你能说出你正在使用的Date构造函数吗?由于 java.util.date 没有这样的构造函数,它将Date作为参数。 请详细说明,以便我可以继续进行调试并回答您的问题。
感谢。