起初我有一个关于嘲笑和存根的一般问题。我真的看不出这两个词之间的区别。 以下是我目前所知道的: 创建单元测试并且要测试的方法调用另一个类的方法时,应使用模拟对象。必须模拟包含被调用方法的类。现在我必须决定是做嘲笑还是存根。
模拟:我只是检查我正在测试的方法是否调用了模拟类中的另一个方法。
存根:由于我不想依赖被调用方法背后的代码,因此我预定义了调用方法时应返回的内容。因此,我能够进行单元测试,即使测试中的方法调用甚至尚未实现的方法。
我很确定,我还没有完全理解关于嘲笑和存根的一切。这可能就是我自己无法解决以下问题的原因。
这是方法,我想为其创建一个单元测试。 getCharge位于一个名为Movie的类中:
double getCharge(int daysRented) {
return price.getCharge(daysRented);
}
这里有一些重要的电影类代码:
public class Movie {
public static final int CHILDRENS = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private Price price;
private String title;
public Movie(String title, int priceCode) {
if (title == null) {
throw new IllegalArgumentException("Title cannot be null");
} else if (title.isBlank()) {
throw new IllegalArgumentException("Title cannot be empty");
}
this.title = title;
this.setPriceCode(priceCode);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
if (title == null) {
throw new IllegalArgumentException("Title cannot be null");
} else if (title.isBlank()) {
throw new IllegalArgumentException("Title cannot be empty");
}
this.title = title;
}
double getCharge(int daysRented) {
return price.getCharge(daysRented);
}
public void setPriceCode(int priceCode) {
switch (priceCode) {
case REGULAR:
price = new RegularPrice();
break;
case CHILDRENS:
price = new ChildrensPrice();
break;
case NEW_RELEASE:
price = new NewReleasePrice();
break;
default:
throw new IllegalArgumentException("Incorrect Price Code");
}
}
public int getFrequentRenterPoints(int daysRented) {
if (daysRented <= 0) {
throw new IllegalArgumentException("Rented days have to be more than 0.");
}
return price.getFrequentRenterPoints(daysRented);
}
}
由于我正在进行单元测试,因此我不想使用价格类的逻辑。因此,我模拟价格类并预定义价格类的 getCharge-Method 应该返回什么:
@Test
public void testGetCharge() {
// given
Price mockedPrice = Mockito.mock(Price.class);
when(mockedPrice.getCharge(3)).thenReturn(3.0);
// when
double expected = 3.0;
double actual = movie.getCharge(3);
assertEquals(expected, actual);
}
显然这是行不通的,因为我没有将我的模拟价格与我的电影类中的价格对象联系起来。问题是我只能使用 setPriceCode 来设置价格对象(参见 Movie 类的构造函数)。这就是我被困住的地方。有什么解决方案可以在不创建另一个二传手方法的情况下设置价格?
Mockito提供了@InjectMocks注释,它做了一些反射魔法,并将任何带有@Mock注释的测试字段"推送"到被测类中。有关一些指导,请参阅此处。
@InjectMocks有点棘手,因为它在未能完成工作时不会给你任何提示,然后它根本不注入模拟对象,你一直想知道为什么你的测试以奇怪的方式运行。
使用该注释的替代方法是"其他依赖注入方式",例如,通过拥有一个构造函数来获取您需要模拟测试的对象。
关于嘲笑与存根...这两个词实际上没有确切的定义。例如,您可以在此处获得一些想法。在那里,您被告知模拟需要一个规范,例如返回什么。对于 mockito,这不一定是正确的,因为当您在模拟对象上调用值返回方法(如 null 或 0)时,Mockito 将返回一些默认值。