Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
418 views
in Technique[技术] by (71.8m points)

unit testing - Mocking void methods using Mockito

I cannot seem to mock void methods on Mockito. It gives a unfinished stubbing detected here error. Here is my classfile.

package com.twu.biblioteca;

import java.io.InputStream;
import java.io.PrintStream;
import java.util.InputMismatchException;
import java.util.Scanner;

public class BibliotecaApp {

public static class IntegerAsker {
    private final Scanner scanner;
    private final PrintStream out;

    public IntegerAsker(InputStream in, PrintStream out) {
        scanner = new Scanner(in);
        this.out = out;
    }

    public int ask(String message) {
        out.print(message);
        return scanner.nextInt();
    }
}

public static int numberOfBooks = 0;

public static class book{
    int serialNo;
    String name;
    String author;
    int publication;
    int checkoutstatus;

    book(){
        serialNo = -1;
        name = null;
        author = null;
        publication = -1;
        checkoutstatus = -1;
    }

    book(int serialNo,String name, String author, int publication){
        this.serialNo = serialNo;
        this.name = name;
        this.author = author;
        this.publication = publication;
        this.checkoutstatus=checkoutstatus = 1;
    }
}

public static int getBoundIntegerFromUser(IntegerAsker asker,String message,int lowerBound,int upperBound) {
    int input;
    try
    {
        input = asker.ask(message);
        while(input>upperBound || input<lowerBound)
            input = asker.ask("Select a valid option! ");
            return input;

    }
    catch(InputMismatchException exception)
    {
        System.out.print("You have selected an invalid option! ");
    }
    return -1;
}

public static book[] booksList = new book[20];


public static String welcome(){
    IntegerAsker asker = new IntegerAsker(System.in,System.out);
    return "**** Welcome Customer! We are glad to have you at Biblioteca! ****";

}

public static void addBooks(){
    book newBook1 = new book(1,"Head First Java","Bert Bates",2014);
    booksList[1] = newBook1;
    numberOfBooks += 1;

    book newBook2 = new book(2,"1000 IT Quizzes","Dheeraj Malhotra",2009);
    booksList[2] = newBook2;
    numberOfBooks += 1;

    book newBook3 = new book(3,"100 Shell Programs in Unix","Shivani Jain",2009);
    booksList[3] = newBook3;
    numberOfBooks += 1;

}

public static void mainMenu(IntegerAsker asker){

    System.out.println("1 " + "List Books");
    System.out.println("2" + " Checkout a Book");
    System.out.println("3 " + "Quit");
    int n = getBoundIntegerFromUser(asker,"Enter your choice. ",1,3);
    mainMenuaction(n,asker);
}

public static void mainMenuaction(int n,IntegerAsker asker){
    if(n==1){
        showBooks();
        mainMenu(asker);
    }
    else if(n==2){
        checkout(asker);
    }
    else if(n==3){
        return;
    }
}

public static void showBooks(){
    for(int i=1;i<=numberOfBooks;i++){
        if(booksList[i].checkoutstatus!=0)
        System.out.println(booksList[i].serialNo + ".	" + booksList[i].name + "	" + booksList[i].author + "	" + booksList[i].publication);
    }
}

public static void checkout(IntegerAsker asker){
    int Input = asker.ask("Enter the serial numebr of the book that you want to checkout");
    if(booksList[Input]!=null){
        if(booksList[Input].checkoutstatus!=0){
            booksList[Input].checkoutstatus=0;
            System.out.println("Thank you! Enjoy the book");
        }
        else{
            System.out.println("That book is not available.");
        }
    }
    else{
        System.out.println("That book is not available.");
    }

    mainMenu(asker);

}


public static void main(String[] args) {
    System.out.println(welcome());
    addBooks();
    IntegerAsker asker = new IntegerAsker(System.in,System.out);
    mainMenu(asker);
}
}

And here goes my test file -

package com.twu.biblioteca;


import org.mockito.Mockito;
import org.mockito.Mockito.*;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;


public class ExampleTest {

BibliotecaApp test = Mockito.mock(BibliotecaApp.class);

@Test
public void welcometest() {
    assertEquals("**** Welcome Customer! We are glad to have you at Biblioteca! ****",test.welcome());
}

@Test
public void addBooksTest(){
    test.addBooks();

    assertEquals("Head First Java",test.booksList[1].name);
    assertEquals("Dheeraj Malhotra",test.booksList[2].author);
    assertEquals(2009,test.booksList[3].publication);
}

@Test
public void getBoundIntegerFromUserTest(){
    BibliotecaApp.IntegerAsker asker = mock(BibliotecaApp.IntegerAsker.class);
    when(asker.ask("Enter your choice. ")).thenReturn(99);
    when(asker.ask("Select a valid option! ")).thenReturn(1);

    BibliotecaApp.getBoundIntegerFromUser(asker,"Enter your choice. ",1,2);

    verify(asker).ask("Select a valid option! ");
}

@Test
public void mainMenuTest(){
    BibliotecaApp.IntegerAsker asker = mock(BibliotecaApp.IntegerAsker.class);

    when(asker.ask("Enter your choice. ")).thenReturn(3);
    test.mainMenu(asker);

    verify(test).mainMenuaction(1,asker);
}

@Test
public void checkoutTest(){
    BibliotecaApp.IntegerAsker asker = mock(BibliotecaApp.IntegerAsker.class);
    BibliotecaApp test = new BibliotecaApp();
    BibliotecaApp mock = spy(test);
    when(asker.ask("Enter the serial numebr of the book that you want to checkout")).thenReturn(2);
    Mockito.doNothing().when(mock).mainMenu(asker);

    test.addBooks();
    test.checkout(asker);


    assertEquals(0,test.booksList[2].checkoutstatus);
}
}

Can someone point out what I am doing wrong please ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
/* system */  public static void mainMenu(IntegerAsker asker){ ... }
/*  test  */  Mockito.doNothing().when(mock).mainMenu(asker);

Your problem isn't about mocking void methods, it's about mocking static methods, which Mockito can't do. Behind the scenes, Mockito is creating an override of your mocked/spied class (BibliotecaApp) to override each of the methods, but because static methods can't be overridden the same way, Mockito can't change mainMenu's behavior—even just to detect that you called it in the stubbing, which is why this shows up as "unfinished stubbing".

Remove the static modifier from mainMenu and you'll be over that hurdle.


Side note: You also spy on a class but keep the original around. This isn't a good idea in Mockito: A spy actually creates a copy of the object, so if you're relying on behavior that applies to the spy, you'll have to call the test methods on the spy. (This is part of the reason to avoid spies in your tests: using spies can blur the line between testing your system's behavior and testing Mockito's behavior.)

BibliotecaApp test = new BibliotecaApp();
BibliotecaApp mock = spy(test);
when(asker.ask("...")).thenReturn(2);
Mockito.doNothing().when(mock).mainMenu(asker);

test.addBooks();           // should be: mock.addBooks()
test.checkout(asker);      // should be: mock.checkout(asker)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...