首页 > 解决方案 > 这怎么可能?(链表实现的Junit异常测试

问题描述

我正在编写一个 LinkedList 实现,其中包括一个先前的函数,该函数返回作为输入参数传递的位置之前的位置。它应该检查输入的位置是否是第一个位置并在这种情况下抛出异常:

@Override
public Position<T> previous (Position<T> p) throws PositionException {
    if (this.first(p)) {
        throw new PositionException();
    }
    return this.convert(p).prev;
}

但是,以下测试失败了,因为它没有在尝试在数组的第一个位置使用前一个函数时抛出预期的异常:

@Test (expected=PositionException.class)
public void gettingPreviousAtFront() {
    Position<String> one = list.insertFront("One");
    Position<String> two = list.insertFront("Two");
    assertTrue(list.first(two));
    Position<String> beforeTwo = list.previous(two);
}

有 1 次失败:1) gettingPreviousAtFront(hw6.test.LinkedListTest) java.lang.AssertionError: Expected exception: exceptions.PositionException at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32)

它甚至在测试的第 301 行通过了“二”是第一个的断言。那么前面的函数怎么可能没有抛出异常呢?

这是完整的链表代码:

package hw6;

import exceptions.EmptyException;
import exceptions.PositionException;

import java.util.Iterator;
import java.util.NoSuchElementException;


public class LinkedList<T> implements List<T> {

    private static final class Node<T> implements Position<T> {
        // The usual doubly-linked list stuff.
        Node<T> next;   // reference to the Node after this
        Node<T> prev;   // reference to the Node before this
        T data;

        // List that created this node, to validate positions.
        List<T> owner;

        @Override
        public T get() {
            return this.data;
        }

        @Override
        public void put(T t) {
            this.data = t;
        }
    }

    /** This iterator can be used to create either a forward
        iterator, or a backwards one.
    */
    private final class ListIterator implements Iterator<T> {
        Node<T> current;
        boolean forward;

        ListIterator(boolean f) {
            this.forward = f;
            if (this.forward) {
                this.current = LinkedList.this.sentinelHead.next;
            } else {
                this.current = LinkedList.this.sentinelTail.prev;
            }
        }

        @Override
        public T next() throws NoSuchElementException {
            if (!this.hasNext()) {
                throw new NoSuchElementException();
            }
            T t = this.current.get();
            if (this.forward) {
                this.current = this.current.next;
            } else {
                this.current = this.current.prev;
            }
            return t;
        }

        @Override
        public boolean hasNext() {
            if (this.forward) {
                return this.current != LinkedList.this.sentinelTail;
            }
            else {
                return this.current != LinkedList.this.sentinelHead;
            }
        }
    }

    /* ** LinkedList instance variables are declared here! ** */

    private Node<T> sentinelHead;
    private Node<T> sentinelTail;
    private int length;             // how many nodes in the list

    /**
     * Create an empty list.
     */
    public LinkedList() {
        this.sentinelHead = new Node<>();
        this.sentinelTail = new Node<>();
        this.sentinelHead.owner = this;
        this.sentinelTail.owner = this;
        this.sentinelTail.prev = this.sentinelHead;
        this.sentinelHead.next = this.sentinelTail;
        this.length = 0;
    }

    // Convert a position back into a node. Guards against null positions,
    // positions from other data structures, and positions that belong to
    // other LinkedList objects. That about covers it?
    private Node<T> convert(Position<T> p) throws PositionException {
        try {
            Node<T> n = (Node<T>) p;
            if (n.owner != this) {
                throw new PositionException();
            }
            return n;
        } catch (NullPointerException | ClassCastException e) {
            throw new PositionException();
        }
    }

    @Override
    public boolean empty() {
        return this.length == 0;
    }

    @Override
    public int length() {
        return this.length;
    }

    @Override
    public boolean first(Position<T> p) throws PositionException {
        Node<T> n = this.convert(p);
        return this.sentinelHead.next == n;
    }

    @Override
    public boolean last(Position<T> p) throws PositionException {
        Node<T> n = this.convert(p);
        return this.sentinelTail.prev == n;
    }

    @Override
    public Position<T> front() throws EmptyException {
        if (this.length == 0) {
            throw new EmptyException();
        }
        return this.sentinelHead.next;
    }

    @Override
    public Position<T> back() throws EmptyException {
        if (this.empty()) {
            throw new EmptyException();
        }
        return this.sentinelTail.prev;
    }

    @Override
    public Position<T> next(Position<T> p) throws PositionException {
        if (this.last(p)) {
            throw new PositionException();
        }
        return this.convert(p).next;
    }

    @Override
    public Position<T> previous(Position<T> p) throws PositionException {
        if (this.first(p)) {
            throw new PositionException();
        }
        return this.convert(p).prev;
    }

    @Override
    public Position<T> insertFront(T t) {
        return this.insertAfter(this.sentinelHead, t);
    }

    @Override
    public Position<T> insertBack(T t) {
        return this.insertBefore(this.sentinelTail, t);
    }

    @Override
    public void removeFront() throws EmptyException {
        this.remove(this.front());
    }

    @Override
    public void removeBack() throws EmptyException {
        this.remove(this.back());
    }

    @Override
    public void remove(Position<T> p) throws PositionException {
        Node<T> n = this.convert(p);
        n.owner = null;
        n.prev.next = n.next;
        n.next.prev = n.prev;
        this.length--;
    }

    @Override
    public Position<T> insertBefore(Position<T> p, T t)
            throws PositionException {
        Node<T> current = this.convert(p);
        Node<T> n = new Node<T>();
        n.owner = this;
        n.data = t;

        n.prev = current.prev;
        current.prev.next = n;
        n.next = current;
        current.prev = n;
        this.length++;
        return n;
    }

    @Override
    public Position<T> insertAfter(Position<T> p, T t)
            throws PositionException {
        Node<T> current = this.convert(p);
        Node<T> n = new Node<T>();
        n.owner = this;
        n.data = t;

        n.next = current.next;
        current.next.prev = n;
        n.prev = current;
        current.next = n;
        this.length++;
        return n;
    }

    @Override
    public Iterator<T> forward() {
        return new ListIterator(true);
    }

    @Override
    public Iterator<T> backward() {
        return new ListIterator(false);
    }

    @Override
    public Iterator<T> iterator() {
        return this.forward();
    }

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append("[");
        for (Node<T> n = this.sentinelHead.next; n != this.sentinelTail; n = n.next) {
            s.append(n.data);
            if (n.next != this.sentinelTail) {
                s.append(", ");
            }
        }
        s.append("]");
        return s.toString();
    }
}

标签: javajunitlinked-list

解决方案


推荐阅读