-2

所以我有可以运行的代码,但是我需要为其中一个类(单元格)编写测试,我不知道该怎么做。

通常,我会在 CellsTest 的开头有一个帮助类,它会封装构造函数调用,它依赖于实现整个程序的类的特定类名。这个助手类将返回该程序的一个实例。

所以,我认为我需要有类似的东西:

    private Maze createMaze() {
            return new Maze();
        }

但是,我的测试方法无法访问 Cell 方法,只能访问 Maze 方法......所以我猜这是错误的,我不确定我应该如何实际执行此操作。

    //Maze.java 
    package falstad;
    import java.awt.*;
    import java.util.ArrayList;
    import java.util.Iterator;

    /**
     * Class handles the user interaction for the maze.
     */
    //public class Maze extends Panel {
    public class Maze  {
            final private ArrayList<Viewer> views = new ArrayList<Viewer>() ;
            MazePanel panel ; // graphics to draw on, shared by all views
            private int state;                     
            private int percentdone = 0; 
            private boolean showMaze;
            private boolean showSolution;
            private boolean solving;
            private boolean mapMode;
            //static final int viewz = 50;    
            int viewx, viewy, angle;
            int dx, dy;
            int px, py ;
            int walkStep;
            int viewdx, viewdy;
            boolean deepdebug = false;
            boolean allVisible = false;
            boolean newGame = false;

            int mazew; // width
            int mazeh; // height
            Cells mazecells ; 
            Distance mazedists ;
            Cells seencells ; 

            BSPNode rootnode ;

            // Mazebuilder is used to calculate a new maze together with a solution
            // The maze is computed in a separate thread. It is started in the local Build method.
            MazeBuilder mazebuilder;

            final int ESCAPE = 27;
            int method = 0 ; // 0 : default method, Falstad's original code

            int zscale = Constants.VIEW_HEIGHT/2;

            private RangeSet rset;

            //Constructor
            public Maze() {
                    super() ;
                    panel = new MazePanel() ;
            }
            //selects a generation method
            public Maze(int method)
            {
                    super() ;
                    if (1 == method)
                            this.method = 1 ;
                    panel = new MazePanel() ;
            }

            public void init() {
                    state = Constants.STATE_TITLE;
                    rset = new RangeSet();
                    panel.initBufferImage() ;
                    addView(new MazeView(this)) ;
                    notifyViewerRedraw() ;
            }

            // Method obtains a new Mazebuilder and computes new maze
            private void build(int skill) {
                    state = Constants.STATE_GENERATING;
                    percentdone = 0;
                    notifyViewerRedraw() ;
                    // select generation method
                    switch(method){
                    case 1 : mazebuilder = new MazeBuilderPrim(); 
                    break ;
                    case 0: 
                    default : mazebuilder = new MazeBuilder();
                    break ;
                    }
                    mazew = Constants.SKILL_X[skill];
                    mazeh = Constants.SKILL_Y[skill];
                    mazebuilder.build(this, mazew, mazeh, Constants.SKILL_ROOMS[skill], Constants.SKILL_PARTCT[skill]);
            }

            //Call back method for MazeBuilder to communicate newly generated maze 
            public void newMaze(BSPNode root, Cells c, Distance dists, int startx, int starty) {
                    if (Cells.deepdebugWall)
                    {   
                            c.saveLogFile(Cells.deepedebugWallFileName);
                    }
                    showMaze = showSolution = solving = false;
                    mazecells = c ;
                    mazedists = dists;
                    seencells = new Cells(mazew+1,mazeh+1) ;
                    rootnode = root ;
                    setCurrentDirection(1, 0) ;
                    setCurrentPosition(startx,starty) ;
                    walkStep = 0;
                    viewdx = dx<<16;
                    viewdy = dy<<16;
                    angle = 0;
                    mapMode = false;
                    state = Constants.STATE_PLAY;
                    cleanViews() ;
                    addView(new FirstPersonDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,
                                    Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, root, this)) ;

                    addView(new MapDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, this)) ;

                    notifyViewerRedraw() ;
            }



            public void addView(Viewer view) {
                    views.add(view) ;
            }
            public void removeView(Viewer view) {
                    views.remove(view) ;
            }
            private void cleanViews() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            if ((v instanceof FirstPersonDrawer)||(v instanceof MapDrawer))
                            {
                                    //System.out.println("Removing " + v);
                                    it.remove() ;
                            }
                    }

            }
            private void notifyViewerRedraw() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;

                            v.redraw(panel.getBufferGraphics(), state, px, py, viewdx, viewdy, walkStep, Constants.VIEW_OFFSET, rset, angle) ;
                    }

                    panel.update() ;
            }
            private void notifyViewerIncrementMapScale() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            v.incrementMapScale() ;
                    }

                    panel.update() ;
            }
            private void notifyViewerDecrementMapScale() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            v.decrementMapScale() ;
                    }

                    panel.update() ;
            }
            boolean isInMapMode() {
                    return mapMode ;
            }
            boolean isInShowMazeMode() {
                    return showMaze ;
            }
            boolean isInShowSolutionMode() {
                    return showSolution ;
            }
            public String getPercentDone(){
                    return String.valueOf(percentdone) ;
            }
            public Panel getPanel() {
                    return panel ;
            }
            private void setCurrentPosition(int x, int y)
            {
                    px = x ;
                    py = y ;
            }
            private void setCurrentDirection(int x, int y)
            {
                    dx = x ;
                    dy = y ;
            }
            void buildInterrupted() {
                    state = Constants.STATE_TITLE;
                    notifyViewerRedraw() ;
                    mazebuilder = null;
            }
            final double radify(int x) {
                    return x*Math.PI/180;
            }
            public boolean increasePercentage(int pc) {
                    if (percentdone < pc && pc < 100) {
                            percentdone = pc;
                            if (state == Constants.STATE_GENERATING)
                            {
                                    notifyViewerRedraw() ;
                            }
                            else
                                    dbg("Warning: Receiving update request for increasePercentage while not in generating state, skip redraw.") ;
                            return true ;
                    }
                    return false ;
            }

            private void dbg(String str) {
                    //System.out.println(str);
            }

            private void logPosition() {
                    if (!deepdebug)
                            return;
                    dbg("x="+viewx/Constants.MAP_UNIT+" ("+
                                    viewx+") y="+viewy/Constants.MAP_UNIT+" ("+viewy+") ang="+
                                    angle+" dx="+dx+" dy="+dy+" "+viewdx+" "+viewdy);
            }
            private boolean checkMove(int dir) {
                    int a = angle/90;
                    if (dir == -1)
                            a = (a+2) & 3;
                    return mazecells.hasMaskedBitsFalse(px, py, Constants.MASKS[a]) ;
            }         
            private void rotateStep() {
                    angle = (angle+1800) % 360;
                    viewdx = (int) (Math.cos(radify(angle))*(1<<16));
                    viewdy = (int) (Math.sin(radify(angle))*(1<<16));
                    moveStep();
            }
            private void moveStep() {
                    notifyViewerRedraw() ;
                    try {
                            Thread.currentThread().sleep(25);
                    } catch (Exception e) { }
            }

            private void rotateFinish() {
                    setCurrentDirection((int) Math.cos(radify(angle)), (int) Math.sin(radify(angle))) ;
                    logPosition();
            }

            private void walkFinish(int dir) {
                    setCurrentPosition(px + dir*dx, py + dir*dy) ;

                    if (isEndPosition(px,py)) {
                            state = Constants.STATE_FINISH;
                            notifyViewerRedraw() ;
                    }
                    walkStep = 0;
                    logPosition();
            }

            private boolean isEndPosition(int x, int y) {
                    return x < 0 || y < 0 || x >= mazew || y >= mazeh;
            }

            synchronized private void walk(int dir) {
                    if (!checkMove(dir))
                            return;
                    for (int step = 0; step != 4; step++) {
                            walkStep += dir;
                            moveStep();
                    }
                    walkFinish(dir);
            }

            synchronized private void rotate(int dir) {
                    final int originalAngle = angle;
                    final int steps = 4;

                    for (int i = 0; i != steps; i++) {
                            angle = originalAngle + dir*(90*(i+1))/steps;
                            rotateStep();
                    }
                    rotateFinish();
            }
           //CLASS CONTROLLING PLAYER MOVEMENTS, REMOVED FOR CHARACTER COUNT
            }
    }


//Cells.java


package falstad;

import java.io.BufferedWriter;
import java.io.FileWriter;

//This class encapsulates all access to a grid of cells
public class Cells {

    private int width;
    private int height ;
    private int[][] cells; 


    public Cells(int w, int h) {
            width = w ;
            height = h ;
            cells = new int[w][h];
    }


    public Cells(int[][] target){
            this(target.length, target[0].length);
            for (int i=0; i<width; i++)
                    for (int j=0; j<height; j++)
                            this.cells[i][j]=target[i][j];
    }

    public int getCells( int x, int y )
    {
            return cells[x][y] ;
    }
    static public int[] getMasks() {
            return Constants.MASKS ;
    }

    public boolean canGo(int x, int y, int dx, int dy) {
            if (hasMaskedBitsTrue(x, y, (getBit(dx, dy) << Constants.CW_BOUND_SHIFT)))
                    return false;
            return isFirstVisit(x+dx, y+dy);
    }
    private int getBit(int dx, int dy) {
            int bit = 0;
            switch (dx + dy * 2) {
            case 1:  bit = Constants.CW_RIGHT; break;
            case -1: bit = Constants.CW_LEFT;  break;
            case 2:  bit = Constants.CW_BOT;   break;
            case -2: bit = Constants.CW_TOP;   break;
            default: dbg("getBit problem "+dx+" "+dy); break;
            }
            return bit;
    }
//CLASSES FOR BITWISE ADJUSTMENTS, REMOVED FOR CHARACTER COUNT



    public void initialize() {
            int x, y;

            for (x = 0; x != width; x++) {
                    for (y = 0; y != height; y++) {
                            setBitToOne(x, y, (Constants.CW_VISITED | Constants.CW_ALL));
                    }
                    setBitToOne(x, 0, Constants.CW_TOP_BOUND);
                    setBitToOne(x, height-1, Constants.CW_BOT_BOUND);
            }
            for (y = 0; y != height; y++) {
                    setBitToOne(0, y, Constants.CW_LEFT_BOUND);
                    setBitToOne(width-1, y, Constants.CW_RIGHT_BOUND);
            }
    }

    public boolean areaOverlapsWithRoom(int rx, int ry, int rxl, int ryl) {
            int x, y;
            for (x = rx-1; x <= rxl+1; x++)
            {
                    for (y = ry-1; y <= ryl+1; y++)
                    {
                            if (isInRoom(x, y))
                                    return true ;
                    }
            }
            return false ;
    }

    private void deleteBound(int x, int y, int dx, int dy) {
            setBoundToZero(x, y, dx, dy);
            setBoundToZero(x+dx, y+dy, -dx, -dy) ;
    }


    public void addBoundWall(int x, int y, int dx, int dy) {

            setBoundAndWallToOne(x, y, dx, dy);
            setBoundAndWallToOne(x+dx, y+dy, -dx, -dy);
    }

    public void deleteWall(int x, int y, int dx, int dy) {
            setWallToZero(x, y, dx, dy);
            setWallToZero(x+dx, y+dy, -dx, -dy);

            if (deepdebugWall) 
                    logWall( x,  y,  dx,  dy);

    public void markAreaAsRoom(int rw, int rh, int rx, int ry, int rxl, int ryl) {
            int x;
            int y;
            for (x = rx; x <= rxl; x++)
                    for (y = ry; y <= ryl; y++) {
                            setAllToZero(x, y);
                            setInRoomToOne(x, y);
                    }
            for (x = rx; x <= rxl; x++) {
                    addBoundWall(x, ry, 0, -1);
                    addBoundWall(x, ryl, 0, 1);
            }
            for (y = ry; y <= ryl; y++) {
                    addBoundWall(rx, y, -1, 0);
                    addBoundWall(rxl, y, 1, 0);
            }
            int wallct = (rw+rh)*2;
            SingleRandom random = SingleRandom.getRandom() ;
            for (int ct = 0; ct != 5; ct++) {
                    int door = random.nextIntWithinInterval(0, wallct-1);
                    int dx, dy;
                    if (door < rw*2) {
                            y = (door < rw) ? 0 : rh-1;
                            dy = (door < rw) ? -1 : 1;
                            x = door % rw;
                            dx = 0;
                    } else {
                            door -= rw*2;
                            x = (door < rh) ? 0 : rw-1;
                            dx = (door < rh) ? -1 : 1;
                            y = door % rh;
                            dy = 0;
                    }
                    deleteBound(x+rx, y+ry, dx, dy);
            }
    }
    public boolean hasMaskedBitsTrue(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) != 0;
    }

    public boolean isInRoom(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_IN_ROOM);
    }
    private boolean isFirstVisit(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_VISITED);
    }
    public boolean hasWallOnRight(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
    }
    public boolean hasWallOnLeft(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
    }
    public boolean hasWallOnTop(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_TOP);
    }
    public boolean hasWallOnBottom(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_BOT);
    }

    public boolean hasNoWallOnBottom(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_BOT);
    }
    public boolean hasNoWallOnTop(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_TOP);
    }
    public boolean hasNoWallOnLeft(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
    }
    public boolean hasNoWallOnRight(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
    }


    public boolean hasMaskedBitsFalse(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) == 0;
    }

    public boolean hasMaskedBitsGTZero(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) > 0;
    }


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

    public String toString() {
            String s = "" ;
            for (int i = 0 ; i < width ; i++)
            {
                    for (int j = 0 ; j < height ; j++)
                            s += " i:" + i + " j:" + j + "=" + cells[i][j] ;
                    s += "\n" ;
            }
            return s ;
    }

    static boolean deepdebugWall = false;
    static final String deepedebugWallFileName = "logDeletedWalls.txt" ;
    StringBuffer traceWall = (deepdebugWall) ? new StringBuffer("x  y  dx  dy\n") : null ;


    private void logWall(int x, int y, int dx, int dy) {
            if (null != traceWall)
            {
                    traceWall.append(x + " " + y + " " + dx + " " + dy + "\n");
            }
    }

    public void saveLogFile( String filename )
    {
            try {  
                    BufferedWriter out = new BufferedWriter(new FileWriter(filename));  
            out.write(traceWall.toString());  
            out.close();
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
    }

}

//MazeBuilder.java 


package falstad;

public class MazeBuilder implements Runnable {
        protected int width, height ;
        Maze maze; 
        private int rooms; 
        int expectedPartiters;  
        protected int startx, starty ; 
        protected Cells cells; 
        protected Distance dists ;
        protected SingleRandom random ;
        Thread buildThread; 
        public MazeBuilder(){
            random = SingleRandom.getRandom();
    }

    public MazeBuilder(boolean deterministic){
            if (true == deterministic)
            {
                    System.out.println("Project 2: functionality to make maze generation deterministic not implemented yet! Fix this!");


            }
            random = SingleRandom.getRandom();
    }


    static int getSign(int num) {
            return (num < 0) ? -1 : (num > 0) ? 1 : 0;
    }


    protected void generate() {
            generatePathways();
            final int[] remote = dists.computeDistances(cells) ;
            final int[] pos = dists.getStartPosition();
            startx = pos[0] ;
            starty = pos[1] ;
            setExitPosition(remote[0], remote[1]);
    }


    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);
                    }
            }
    }

    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 ;

    private boolean placeRoom() {
            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;

            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;
            if (cells.areaOverlapsWithRoom(rx, ry, rxl, ryl))
                    return false ;
            cells.markAreaAsRoom(rw, rh, rx, ry, rxl, ryl);
            return true;
    }

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



    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();
    }
    private void init(Maze mz, int w, int h, int roomct, int pc) {
            maze = mz;
            width = w;
            height = h;
            rooms = roomct;
            expectedPartiters = pc;
            cells = new Cells(w,h) ;
            dists = new Distance(w,h) ;
            //colchange = random.nextIntWithinInterval(0, 255); 
    }

    static final long SLEEP_INTERVAL = 100 ;
            try {
                    cells.initialize();
                    // rooms into maze
                    generateRooms();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    // pathways into the maze, 
                    generate();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    final int colchange = random.nextIntWithinInterval(0, 255);
                    final BSPBuilder b = new BSPBuilder(maze, dists, cells, width, height, colchange, expectedPartiters) ;
                    BSPNode root = b.generateBSPNodes();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    // dbg("partiters = "+partiters);
                    maze.newMaze(root, cells, dists, startx, starty);
            }
            catch (InterruptedException ex) {
                    // dbg("Catching signal to stop") ;
            }
    }

    static final int MAX_TRIES = 250 ;


    private int generateRooms() {
            int tries = 0 ;
            int result = 0 ;
            while (tries < MAX_TRIES && result <= rooms) {
                    if (placeRoom())
                            result++ ;
                    else
                            tries++ ;
            }
            return result ;
    }

    public void interrupt() {
            buildThread.interrupt() ;
    }
}

显然,我并不是要求有人为我写一个测试——你们中的任何人都不会这样做,因为这显然是一项家庭作业——但我希望得到一些帮助。因此,如果有人能提示我了解我所缺少的明显的技巧,那就太好了!

4

1 回答 1

1

类的单元测试Cells可能如下所示:当您对Cells可能不需要Mazeor的类进行单元测试时MazeBuilder,您只需要一个类的实例Cells并调用其方法以检查它们是否与应用程序的其余部分隔离开来.

还要看看这个:Java 中的单元测试——它是什么?因为它提供了很好的参考。

import org.junit.Assert;
import org.junit.Test;

public class CellsTest {

    @Test
    void testHasWallOnRight() {
        //setup
        int[][] target = new int[][] { { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 } };

        Cells a = new Cells(target);

        //act
        boolean result = a.hasWallOnRight(1,1);

        //assert
        Assert.assertFalse(result);
    }
}
于 2013-09-22T20:14:14.920 回答