简体   繁体   中英

Java Unit Test on method with input

When I use the method "execute(p)" there is a list when I must chose an item :

@Override
public void execute(Player p) {
    // listchoser with items available
    ListChoser lc = new ListChoser();
    Object itemChosen;
    itemChosen = lc.chose("Which item?", p.getCurrentRoom().getItems());
    System.out.println("You chose " + itemChosen.toString());
    // put item effect on player
    ((Item) itemChosen).effect(p);
    // remove item from current room
    p.getCurrentRoom().removeItem(itemChosen);
}

I must do some Unit Tests on it so here how I proceed :

Player c;
Action useAction;

@Before
public void initiliaze() {
    c = new Player("Test", 100, 100,100);
    c.setCurrentRoom(new Room("Debug Room", false));
    useAction = new UseAction();
}

@Test
public void testUseAction(){
    List<? super Item> l = c.getCurrentRoom().getItems();
    int nbItems = l.size();
    useAction.execute(c);
    assertEquals(nbItems-1, c.getCurrentRoom().getItems().size());
}

I have some trouble to deal with such tests when I launch them : I'm asked for an input when I should only use a default one (like the 0 one)

I recommend spliting your execute() method into 2 separate methods so first of them only takes an input, processes it and passes a result of an operation to the second one that contains all the rest of logic.

That makes your code consistient with SOLID 's Single responsibility principle and also enables testing that part of code that is responsible for the logic, without taking any input from user(you can just pass some prepared object)

Interactive code and unit testing do not match well. That is why a test-first approach helps to write clean and testable code. If writing test code afterwards you are forced to spend time on rewriting the program code for testability.

A quick and dirty solution is to add another method and call it from the existing one like this:

@Override
public void execute(Player p) {
    execute(Player p, new ListChoser());
}

public void execute(Player p, ListChoser lc) {
    Object itemChosen;
    if (lc != null) {
        itemChosen = lc.chose("Which item?", p.getCurrentRoom().getItems());
        System.out.println("You chose " + itemChosen.toString());
    }
    else { //add exception handling if list is empty
        itemChosen = p.getCurrentRoom().getItems()[0];
    }
    // put item effect on player
    ((Item) itemChosen).effect(p);
    // remove item from current room
    p.getCurrentRoom().removeItem(itemChosen);
}

And use this new method for your test, for example like this:

@Test
public void testUseAction(){
    List<? super Item> l = c.getCurrentRoom().getItems();
    int nbItems = l.size();
    useAction.execute(c, null); //use null for default behaviour
    assertEquals(nbItems-1, c.getCurrentRoom().getItems().size());
} 

That way you can at least test the non-interactive part of your code.

A better solution would be a complete redesign of ListChoser as an interface and implement an InteractiveListChoser and a TestListChoser . Separating type (=interface) from implementation (=class) is a design principle that works well for unit-testing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM