You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java PricedHotel类JUnit测试失败:对象引用不一致求助

Java作业问题:PricedHotel类最后一个断言测试失败排查与修复

我正在完成一个涉及多个类的Java作业,测试文件为给定且不可修改的版本。被测试主类是PricedHotel,目前所有测试仅最后一个断言未通过。调试后发现,PricedHotel类中的room和safe对象与Test类中的对应对象不一致,虽已尝试正确引用,但找不到问题根源,寻求技术帮助。


现有代码

PricedHotel类

package ss.week2.hotel;

import ss.week2.hotel.bill.Bill;
import ss.week2.hotel.bill.BillPrinter;
import ss.week2.hotel.bill.StringBillPrinter;
import ss.week2.hotel.PricedRoom;

public class PricedHotel extends Hotel {

    public static final double ROOM_PRICE = 100.0;
    public static final double SAFE_PRICE = 20.0;
    public PricedRoom r1;
    public Room r2;

    private Guest g1;
    private Guest g2;
    private StringBillPrinter stringBillPrinter;

    public PricedHotel(String name){
        super(name);
        r1 = new PricedRoom(1,ROOM_PRICE,SAFE_PRICE);
        r2 = new Room(2, new Safe());
        stringBillPrinter = new StringBillPrinter();
    }

   @Override
    public Room checkIn(String guestName) {
        Room room = super.checkIn(guestName);

        if (!(room instanceof PricedRoom)) {
            PricedRoom pricedRoom = new PricedRoom(room.getNumber(), ROOM_PRICE, SAFE_PRICE);
            pricedRoom.setGuest(room.getGuest());
            room = pricedRoom;
        }

        return room;
    }


    public Bill getBill(String guestName, int total, BillPrinter billPrinter){
        PricedRoom room = this.r1;
        PricedSafe safe = (PricedSafe) this.r1.getSafe();
        Bill bill = new Bill(billPrinter);

        if (safe.isActive()) {
            Bill.Item item1 = new Bill.Item() {
                @Override
                public double getPrice() {
                    return ((total*ROOM_PRICE) + SAFE_PRICE);
                }

                @Override
                public String toString() {
                    return guestName;
                }
            };
        } else {
            Bill.Item item1 = new Bill.Item() {
                @Override
                public double getPrice() {
                    return total*ROOM_PRICE;
                }

                @Override
                public String toString() {
                    return guestName;
                }
            };
            bill.addItem(item1);
        }
        return bill;
    }
}

Test类

package ss.week2.hotel.tests;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import ss.week2.hotel.*;
import ss.week2.hotel.bill.Bill;
import ss.week2.hotel.bill.StringBillPrinter;
import ss.week2.hotel.password.Password;

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

public class PricedHotelTest {
    /** Test variable for a <tt>Hotel</tt> object. */
    private PricedHotel hotel;
    private String correctPassword;
    private String wrongPassword;
    public static final String GUEST_NAME_1 = "Major Gowen";
    public static final String GUEST_NAME_2 = "Miss Tibbs";
    public static final String GUEST_NAME_3 = "Miss Gatsby";

    public static final int NUMBER_OF_NIGHTS = 5;

    /**
     * Sets the instance variable <tt>hotel</tt> to a well-defined initial value.
     * All test methods should be preceded by a call to this method.
     */
    @BeforeEach
    public void setUp() {
        hotel = new PricedHotel("Fawlty Towers");

        // initialisation of password-variable
        correctPassword = new Password().getInitPass();
        wrongPassword = correctPassword + "_invalid";
    }

    /**
     * checkIn First room should be a PricedRoom
     */
    @Test
    public void testCheckIn() {
        Room room = hotel.checkIn(GUEST_NAME_1);
        assertTrue(room instanceof PricedRoom,
                "room should be an instance of PricedRoom");
        Safe safe = room.getSafe();
        assertTrue(safe instanceof PricedSafe,
                "safe should be an instance of PricedSafe");
    }

    /**
     * If there is a free room, getFreeRoom must return a room without guest.
     */
    @Test
    public void testGetFreeRoomFromNotFullHotel() {
        Room room = hotel.getFreeRoom();
        assertNull(room.getGuest(), "A room is free");
        hotel.checkIn(GUEST_NAME_1);
        Room freeRoom = hotel.getFreeRoom();
        assertNotNull(freeRoom, "Another room is free");
        assertNotEquals(room, freeRoom, "Another room is free");
    }

    /**
     * If there is no free room, getFreeRoom must return null.
     */
    @Test
    public void testGetFreeRoomFromFullHotel() {
        hotel.checkIn(GUEST_NAME_1);
        hotel.checkIn(GUEST_NAME_2);

        Room noRoom = hotel.getFreeRoom();
        assertNull(noRoom, "No room available in a full hotel");
    }

    /**
     * getRoom must not return any room, if the guest is not checked in
     */
    @Test
    public void testGetRoomBeforeCheckIn() {
        Room room = hotel.getRoom(GUEST_NAME_1);
        assertNull(room, "Guest 1 not checked in");
    }

    /**
     * If the guest is checked in, the returned room must be occupied by the specified guest.
     */
    @Test
    public void testGetRoomAfterCheckIn() {
        hotel.checkIn(GUEST_NAME_1);
        Room room = hotel.getRoom(GUEST_NAME_1);
        assertEquals(GUEST_NAME_1, room.getGuest().getName(), "Guest 1 checked in");
    }

    /**
     * A password object must be returned.
     */
    @Test
    public void testGetPassword() {
        Room room = hotel.checkIn(GUEST_NAME_1);
        assertTrue(room instanceof PricedRoom,
                "room should be an instance of PricedRoom");
        Safe safe = room.getSafe();
        assertTrue(safe instanceof PricedSafe,
                "safe should be an instance of PricedSafe");

        Password password = ((PricedSafe) safe).getPassword();
        assertNotNull(password, "Returned password is not null");
    }

    /**
     * The Bill should be correctly calculated
     */
    @Test
    public void testGetBill() {
        Room room = hotel.checkIn(GUEST_NAME_1);
        assertTrue(room instanceof PricedRoom,
                "room should be an instance of PricedRoom");
        StringBillPrinter printer = new StringBillPrinter();
        Bill bill = hotel.getBill(GUEST_NAME_1, NUMBER_OF_NIGHTS, printer);
        double nightsTotal = NUMBER_OF_NIGHTS * PricedHotel.ROOM_PRICE;
        assertEquals(nightsTotal, bill.getSum(), 0.01, "Bill sum should contain only room costs.");
        assertTrue(printer.getResult().contains(String.format("%.2f", nightsTotal)));

        // Now check sum again when safe is actually active
        assertTrue(room.getSafe() instanceof PricedSafe);
        PricedSafe safe = (PricedSafe) room.getSafe();
        assertFalse(safe.isActive(), "Safe should be deactivated after initialisation.");
        safe.activate(correctPassword);
        assertTrue(safe.isActive(), "Safe should be activated after activation with password.");
        Assertions.assertEquals(nightsTotal + PricedHotel.SAFE_PRICE,
                hotel.getBill(GUEST_NAME_1, NUMBER_OF_NIGHTS, printer).getSum());

    }
}

Hotel.checkIn方法

public Room checkIn(String name){
        if (room1.getGuest() != null && room2.getGuest() != null){ //checks if the two rooms are empty
            return null;
        }

        if (g1 != null && Objects.equals(g1.getName(), name)){ //checks if g1 is already checked in
            return null;
        }
        if (g2 != null && Objects.equals(g2.getName(), name)){ //checks if g2 is already checked in
            return null;
        }

        Room freeRoom = getFreeRoom(); //gets a free room
        if (freeRoom == null){ // checks if hotel is fill
            return null;
        }

        Guest newGuest = new Guest(name); //new guest object with the name we provided
        newGuest.setRoom(freeRoom); // set's the new guest to the free room
        freeRoom.setGuest(newGuest); // set's the guest to the free room

        if (g1 == null){
            g1 = newGuest;
        } else if (g2 == null) {
            g2 = newGuest;
        }
        return freeRoom;
    }

问题根源分析

  1. 成员变量重复定义导致对象不一致:
    PricedHotel类中自行定义了r1、r2、g1、g2,但父类Hotel已经有对应的room1、room2、g1、g2成员。父类的checkIn方法操作的是父类的room1/room2,而PricedHotel的getBill方法直接使用自己定义的r1,这两个是完全独立的对象。Test类中激活的是父类room1对应的safe,而getBill检查的是子类r1的safe,自然状态不一致。

  2. checkIn方法未更新父类房间引用:
    子类checkIn方法中,当父类返回的房间不是PricedRoom时,新建了一个PricedRoom对象,但仅修改了局部变量room,并未更新父类中对应的room1/room2引用。导致父类存储的还是原来的Room对象,后续操作的对象与getBill中使用的对象不匹配。

  3. getBill方法的硬编码与逻辑遗漏:

    • 不管传入的guestName是谁,getBill都硬编码使用this.r1,无法正确获取对应客人的房间;
    • 当safe处于激活状态时,创建了Bill.Item但未添加到bill中,导致此时bill的sum为0,直接触发断言失败。

修复方案

步骤1:移除子类重复定义的成员变量

删除PricedHotel中的r1、r2、g1、g2,直接复用父类的成员变量(确保父类的这些成员是protected或public,若父类是private则需要提供getter方法)。

步骤2:修改PricedHotel构造方法,初始化父类房间

public PricedHotel(String name){
    super(name);
    // 直接初始化父类的room1和room2
    room1 = new PricedRoom(1, ROOM_PRICE, SAFE_PRICE);
    room2 = new Room(2, new Safe());
    stringBillPrinter = new StringBillPrinter();
}

步骤3:修正checkIn方法,更新父类房间引用

@Override
public Room checkIn(String guestName) {
    Room room = super.checkIn(guestName);
    if (room == null) {
        return null;
    }
    // 如果返回的房间不是PricedRoom,替换父类中对应的房间
    if (!(room instanceof PricedRoom)) {
        PricedRoom pricedRoom = new PricedRoom(room.getNumber(), ROOM_PRICE, SAFE_PRICE);
        pricedRoom.setGuest(room.getGuest());
        // 根据房间号替换父类的room1或room2
        if (room.getNumber() == 1) {
            room1 = pricedRoom;
        } else if (room.getNumber() == 2) {
            room2 = pricedRoom;
        }
        room = pricedRoom;
    }
    return room;
}

步骤4:修复getBill方法的逻辑问题

public Bill getBill(String guestName, int total, BillPrinter billPrinter){
    // 根据guestName找到对应的房间
    Room guestRoom = getRoom(guestName);
    if (!(guestRoom instanceof PricedRoom)) {
        // 若不是PricedRoom,按需求处理(这里直接返回空bill)
        return new Bill(billPrinter);
    }
    PricedRoom room = (PricedRoom) guestRoom;
    PricedSafe safe = (PricedSafe) room.getSafe();
    Bill bill = new Bill(billPrinter);

    Bill.Item item1 = new Bill.Item() {
        @Override
        public double getPrice() {
            if (safe.isActive()) {
                return total * ROOM_PRICE + SAFE_PRICE;
            } else {
                return total * ROOM_PRICE;
            }
        }

        @Override
        public String toString() {
            return guestName;
        }
    };
    // 无论safe是否激活,都要添加item到bill
    bill.addItem(item1);
    return bill;
}

验证修复

修复后,Test类中激活的safe与getBill中检查的safe是同一个对象,同时getBill会根据客人获取对应房间,且无论safe状态都会正确添加计费项,最后一个断言测试将通过。

内容的提问来源于stack exchange,提问作者ST Saksham Singh Birla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 02:57:35