我有时使用FizzBuzz向新手演示单元测试的基础。 尽管FizzBuzz确实是一个简单的问题,但是它也可以用于演示更高级的单元测试技术,例如模拟 。
FizzBuzz Kata:
“ 编写一个程序,打印从1到100的数字。但是,对于三个数字的倍数,请打印“ Fizz”,而不是数字;对于五个数字的倍数,请打印“ Buzz”。 对于三和五的倍数的数字,请打印“ FizzBuzz ”。
FizzBuzz算法的可能解决方案:
public class FizzBuzz {
private static final int FIVE = 5;
private static final int THREE = 3;
public String calculate(int number) {
if (isDivisibleBy(number, THREE) && isDivisibleBy(number, FIVE)) {
return "FizzBuzz";
}
if (isDivisibleBy(number, THREE)) {
return "Fizz";
}
if (isDivisibleBy(number, FIVE)) {
return "Buzz";
}
return "" + number;
}
private boolean isDivisibleBy(int dividend, int divisor) {
return dividend % divisor == 0;
}
}
由于上面的代码解决了FizzBuzz算法,因此无法解决FizzBuzz问题。 要完成它,我们需要使用该算法的代码来打印从1到100的数字。 代码的这一部分可以用来展示在JUnit中使用Mockito进行模拟的想法。
作为此练习的结果,我最终得到了一个NumberPrinter
,它带有两个参数: Printer
和NumberCalculator
并且具有一个公共方法来打印数字:
public class NumberPrinter {
private NumberCalculator numberCalculator;
private Printer printer;
public NumberPrinter(NumberCalculator numberCalculator, Printer printer) {
this.numberCalculator = numberCalculator;
this.printer = printer;
}
public void printNumbers(int limit) {
if (limit < 1) {
throw new RuntimeException("limit must be >= 1");
}
for (int i = 1; i <= limit; i++) {
try {
printer.print(numberCalculator.calculate(i));
} catch (Exception e) {
// noop
}
}
}
}
public interface NumberCalculator {
String calculate(int number);
}
public interface Printer {
void print(String s);
}
引入接口后,我不仅拥有可测试的代码,而且拥有更强大的代码。 为了测试NumberPrinter
我只是使用Mockito的强大功能和简单性来模拟依赖项。 使用Mockito批注,配置测试代码的读取效果更好。
Mockito功能展示:
- 创建和注入模拟
- 存根方法还可以为连续的方法调用设置不同的行为。
- 将void方法存入一个异常
- 验证
使用的注释:
-
@RunWith(MockitoJUnitRunner.class)
–在每个测试方法之前初始化@Mock
-
@Mock
–将字段标记为模拟 -
@InjectMocks
–标记应在其上执行注射的字段
@RunWith(MockitoJUnitRunner.class)
public class NumberPrinterTest {
@Mock
private Printer printer;
@Mock
private NumberCalculator numberCalculator;
@InjectMocks
private NumberPrinter numberPrinter;
@Test
public void printsCalculatorResultsHundredTimes() {
// arrange
int limit = 100;
when(numberCalculator.calculate(anyInt()))
.thenReturn("0") // first invocation returns "0"
.thenReturn("1"); // other invocations return "1"
// act
numberPrinter.printNumbers(limit);
// assert
verify(numberCalculator, times(limit)).calculate(anyInt());
verify(printer, times(1)).print("0");
verify(printer, times(limit - 1)).print("1");
verifyNoMoreInteractions(numberCalculator, printer);
}
@Test
public void continuesOnCalculatorOrPrinterError() {
// arrange
when(numberCalculator.calculate(anyInt()))
.thenReturn("1")
.thenThrow(new RuntimeException())
.thenReturn("3");
// stub the void method with an exception
doThrow(new RuntimeException()).when(printer).print("3");
// act
numberPrinter.printNumbers(3);
// assert
verify(numberCalculator, times(3)).calculate(anyInt());
verify(printer).print("1");
verify(printer).print("3");
verifyNoMoreInteractions(numberCalculator, printer);
}
}
享受Mockito !
- 想更多地了解Mockito注释? 看看Eugen Paraschiv的“ Mockito – @ Mock,@ Spy,@ Captor和@InjectMocks”: http : //www.baeldung.com/mockito-annotations
- 寻找代码样本? 看一下介绍单元测试不同方面的unit-testing-demo项目,包括模拟: https : //github.com/kolorobot/unit-testing-demo
翻译自: https://www.javacodegeeks.com/2014/11/unit-testing-exercise-with-fizzbuzz-and-mockito.html