4

私はこのコードのチャンクをテストしようとしています - それは MazeBuilder と呼ばれるクラスです。私の問題は、ほとんどのメソッドが保護されているため、テストでそれらにアクセスできないことです...

だから私の考えでは、テストは Run() だけに焦点を当てて、他の多くのメソッドにアクセスするべきだということでした。しかし、1 つの方法だけであらゆる種類の結合テストを実行することは不可能になるのではないかと懸念しています。

さらに、2 つのコンストラクター ( MazeBuilder() および MazeBuilder(boolean deterministic) ) をテストする適切な方法は何ですか? 現状では、形成されたオブジェクトが null ではないこと、つまり、オブジェクトがまったく構築されていることをテストしているだけです。私が知らないコンストラクターをテストするより広範な方法はありますか?

package falstad;

public class MazeBuilder implements Runnable {
    // Given input information: 
    protected int width, height ;   // width and height of maze, 
    Maze maze;              // reference to the maze that is constructed, results are returned by calling maze.newMaze(..)
    private int rooms;      // requested number of rooms in maze, a room is an area with no walls larger than a single cell
    int expectedPartiters;  // user given limit for partiters

    // Produced output information to create the new maze
    // root, cells, dists, startx, starty
    protected int startx, starty ; // starting position inside maze for entity to search for exit
    // conventional encoding of maze as a 2 dimensional integer array encapsulated in the Cells class
    // a single integer entry can hold information on walls, borders/bounds
    protected Cells cells; // the internal representation of a maze as a matrix of cells
    protected Distance dists ; // distance matrix that stores how far each position is away from the exit positino

    // class internal local variables
    protected SingleRandom random ; // random number stream, used to make randomized decisions, e.g for direction to go

    Thread buildThread; // computations are performed in own separated thread with this.run()

    //int colchange; // randomly selected in run method of this thread, used as parameter to Segment class constructor

    /**
     * Constructor for a randomized maze generation
     */
    public MazeBuilder(){
        random = SingleRandom.getRandom();
    }
    /**
     * Constructor with option to make maze generation deterministic or random
     */
    public MazeBuilder(boolean deterministic){
        if (true == deterministic)
        {
            System.out.println("Project 2: functionality to make maze generation deterministic not implemented yet! Fix this!");
            // Control random number generation
            // TODO: implement code that makes sure that if MazeBuilder.build is called for same skill level twice, same results
            // HINT: check http://download.oracle.com/javase/6/docs/api/java/util/Random.html and file SingleRandom.java

        }
        random = SingleRandom.getRandom();
    }

    /**
     * Provides the sign of a given integer number
     * @param num
     * @return -1 if num < 0, 0 if num == 0, 1 if num > 0
     */
    static int getSign(int num) {
        return (num < 0) ? -1 : (num > 0) ? 1 : 0;
    }

    /**
     * This method generates a maze.
     * It computes distances, determines a start and exit position that are as far apart as possible. 
     */
    protected void generate() {
        // generate paths in cells such that there is one strongly connected component
        // i.e. between any two cells in the maze there is a path to get from one to the other
        // the search algorithm starts at some random point
        generatePathways(); 

        final int[] remote = dists.computeDistances(cells) ;

        // identify cell with the greatest distance
        final int[] pos = dists.getStartPosition();
        startx = pos[0] ;
        starty = pos[1] ;

        // make exit position at true exit in the cells data structure
        setExitPosition(remote[0], remote[1]);
    }

    /**
     * This method generates pathways into the maze.
     * 
     */
    protected void generatePathways() {
        int[][] origdirs = new int[width][height] ; 

        int x = random.nextIntWithinInterval(0, width-1) ;
        int y = 0; 
        final int firstx = x ; 
        final int firsty = y ;
        int dir = 0;        
        int origdir = dir;  
        cells.setVisitedFlagToZero(x, y); 
        while (true) {      

            int dx = Constants.DIRS_X[dir];
            int dy = Constants.DIRS_Y[dir];

            if (!cells.canGo(x, y, dx, dy)) { 

                dir = (dir+1) & 3; 
                if (origdir == dir) { 

                    if (x == firstx && y == firsty)
                        break; 


                    int odr = origdirs[x][y];
                    dx = Constants.DIRS_X[odr];
                    dy = Constants.DIRS_Y[odr];

                    x -= dx;
                    y -= dy;



                    origdir = dir = random.nextIntWithinInterval(0, 3);
                }
            } else {


                cells.deleteWall(x, y, dx, dy);

                x += dx;
                y += dy;

                cells.setVisitedFlagToZero(x, y);

                origdirs[x][y] = dir;
                origdir = dir = random.nextIntWithinInterval(0, 3);
            }
        }
    }
    /**
     * Establish valid exit position by breaking down wall to outside area.
     * @param remotex
     * @param remotey
     */
    protected void setExitPosition(int remotex, int remotey) {
        int bit = 0;
        if (remotex == 0)
            bit = Constants.CW_LEFT;
        else if (remotex == width-1)
            bit = Constants.CW_RIGHT;
        else if (remotey == 0)
            bit = Constants.CW_TOP;
        else if (remotey == height-1)
            bit = Constants.CW_BOT;
        else
            dbg("Generate 1");
        cells.setBitToZero(remotex, remotey, bit);
        //System.out.println("exit position set to zero: " + remotex + " " + remotey + " " + bit + ":" + cells.hasMaskedBitsFalse(remotex, remotey, bit)
        //      + ", Corner case: " + ((0 == remotex && 0 == remotey) || (0 == remotex &&  height-1 == remotey) || (width-1 == remotex && 0 == remotey) || (width-1 == remotex && height-1 == remotey)));
    }


    static final int MIN_ROOM_DIMENSION = 3 ;
    static final int MAX_ROOM_DIMENSION = 8 ;
    /**
     * Allocates space for a room of random dimensions in the maze.
     * The position of the room is chosen randomly. The method is not sophisticated 
     * such that the attempt may fail even if the maze has ample space to accommodate 
     * a room of the chosen size. 
     * @return true if room is successfully placed, false otherwise
     */
    private boolean placeRoom() {
        // get width and height of random size that are not too large
        // if too large return as a failed attempt
        final int rw = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
        if (rw >= width-4)
            return false;

        final int rh = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
        if (rh >= height-4)
            return false;

        // proceed for a given width and height
        // obtain a random position (rx,ry) such that room is located on as a rectangle with (rx,ry) and (rxl,ryl) as corner points
        // upper bound is chosen such that width and height of room fits maze area.
        final int rx = random.nextIntWithinInterval(1, width-rw-1);
        final int ry = random.nextIntWithinInterval(1, height-rh-1);
        final int rxl = rx+rw-1;
        final int ryl = ry+rh-1;
        // check all cells in this area if they already belong to a room
        // if this is the case, return false for a failed attempt
        if (cells.areaOverlapsWithRoom(rx, ry, rxl, ryl))
            return false ;
        // since the area is available, mark it for this room and remove all walls
        // from this on it is clear that we can place the room on the maze
        cells.markAreaAsRoom(rw, rh, rx, ry, rxl, ryl); 
        return true;
    }

    static void dbg(String str) {
        System.out.println("MazeBuilder: "+str);
    }



    /**
     * Fill the given maze object with a newly computed maze according to parameter settings
     * @param mz maze to be filled
     * @param w width of requested maze
     * @param h height of requested maze
     * @param roomct number of rooms
     * @param pc number of expected partiters
     */
    public void build(Maze mz, int w, int h, int roomct, int pc) {
        init(mz, w, h, roomct, pc);
        buildThread = new Thread(this);
        buildThread.start();
    }

    /**
     * Initialize internal attributes, method is called by build() when input parameters are provided
     * @param mz maze to be filled
     * @param w width of requested maze
     * @param h height of requested maze
     * @param roomct number of rooms
     * @param pc number of expected partiters
     */
    private void init(Maze mz, int w, int h, int roomct, int pc) {
        // store parameters
        maze = mz;
        width = w;
        height = h;
        rooms = roomct;
        expectedPartiters = pc;
        // initialize data structures
        cells = new Cells(w,h) ;
        dists = new Distance(w,h) ;
        //colchange = random.nextIntWithinInterval(0, 255); // used in the constructor for Segments  class Seg
    }

    static final long SLEEP_INTERVAL = 100 ; //unit is millisecond
    /**
     * Main method to run construction of a new maze with a MazeBuilder in a thread of its own.
     * This method is called internally by the build method when it sets up and starts a new thread for this object.
     */
    public void run() {
        // try-catch block to recognize if thread is interrupted
        try {
            // create an initial invalid maze where all walls and borders are up
            cells.initialize();
            // place rooms in maze
            generateRooms();

            Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop

            // put pathways into the maze, determine its starting and end position and calculate distances
            generate();

            Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop

            final int colchange = random.nextIntWithinInterval(0, 255); // used in the constructor for Segments  class Seg
            final BSPBuilder b = new BSPBuilder(maze, dists, cells, width, height, colchange, expectedPartiters) ;
            BSPNode root = b.generateBSPNodes();

            Thread.sleep(SLEEP_INTERVAL) ; // test if thread has been interrupted, i.e. notified to stop

            // dbg("partiters = "+partiters);
            // communicate results back to maze object
            maze.newMaze(root, cells, dists, startx, starty);
        }
        catch (InterruptedException ex) {
            // necessary to catch exception to avoid escalation
            // exception mechanism basically used to exit method in a controlled way
            // no need to clean up internal data structures
            // dbg("Catching signal to stop") ;
        }
    }

    static final int MAX_TRIES = 250 ;

    /**
     * Generate all rooms in a given maze where initially all walls are up. Rooms are placed randomly and of random sizes
     * such that the maze can turn out to be too small to accommodate the requested number of rooms (class attribute rooms). 
     * In that case less rooms are produced.
     * @return generated number of rooms
     */
    private int generateRooms() {
        // Rooms are randomly positioned such that it may be impossible to place the all rooms if the maze is too small
        // to prevent an infinite loop we limit the number of failed to MAX_TRIES == 250
        int tries = 0 ;
        int result = 0 ;
        while (tries < MAX_TRIES && result <= rooms) {
            if (placeRoom())
                result++ ;
            else
                tries++ ;
        }
        return result ;
    }

    /**
     * Notify the maze builder thread to stop the creation of a maze and to terminate
     */
    public void interrupt() {
        buildThread.interrupt() ;
    }



}
4

2 に答える 2

7

保護されたメソッドを単体テストするには、テスト対象のクラス (この場合はfalsted) と同じパッケージにテスト クラスを配置するだけです。それらが同じパッケージにあるからといって、それらが同じディレクトリにある必要があるという意味ではありません (単に並列テストのディレクトリ階層です)。

たとえば、maven を使用している場合、ソースは にsrc/main/java/falstedあり、テストは にありますsrc/test/java/falsted。Maven の観点からは、これらは個別のディレクトリであるため、簡単に個別に管理できますが、Java の観点からは、これらは同じパッケージです (したがって、保護されたメソッドが表示されます)。

オブジェクトの状態を調べてコンストラクターをテストし、すべての値が既定値または初期値になっていることを確認します。

于 2013-09-24T21:00:19.887 に答える
0

テストメソッドで保護を使用できます。プロジェクトを構築するための推奨される方法は次のとおりです。

src/main/java 内

package falstad;

   public class MazeBuilder {}

src/test/java 内

package falstad;

   public class MazeBuilderTest {}
于 2013-09-24T21:18:47.123 に答える