如何模拟从 class 级对象调用的方法
How to mock a method which is being called from class level object
我正在为 class A 编写单元测试,我想模拟一个方法,但该方法是从 class 级别对象调用的良性方法,我将如何模拟它。
我举个例子解释一下
ClassA正在测试中
public class ClassA {
ClassB objectOfB = new ClassB();
public int add(int a, int b) {
int addition = objectOfB.performCalculation(a,b);
return addition;
}
}
Class B,有一定的业务逻辑。
public class ClassB {
public int performCalculation(int a, int b) {
int c = a+b;
System.out.println("I am not mocked, I am actual call");
System.out.println("Returning " + c + " From ClassB");
return c;
}
}
笔试
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class, ClassB.class})
public class ClassATest {
@InjectMocks
ClassA objA = new ClassA();
@Test
public void testAddFromClassA() throws Exception {
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
int result = objA.add(5, 10);
assertEquals(result, 15);
}
}
测试结果:
这个测试是通过的,但它不是模拟ClassB的方法,而是执行实际调用。
要求:
在编写测试时,我想模拟行:objectOfB.performCalculation(a,b);来自 class A,但如您所见,classB() 的对象是在 class 级别上创建的。
我如何模拟这个?
我应该在测试中写什么 class。
模拟 class 的初始化,以便在执行测试时使用模拟
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class}) //prepare the class creating the new instance of ClassB for test, not the ClassB itself.
public class ClassATest {
@Test
public void testAddFromClassA() throws Exception {
int expected = 15;
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);
//mocking initialization of ClassB class withing ClassA class
PowerMockito.whenNew(ClassB.class).withNoArguments().thenReturn(objB);
ClassA objA = new ClassA();
//Act
int actual = objA.add(5, 10);
//Assert
assertEquals(expected, actual);
}
}
引用How to mock construction of new objects
话虽如此,理想情况下目标 class 应该通过构造函数注入遵循显式依赖原则
public class ClassA {
final ClassB objectOfB;
public ClassA(ClassB objectOfB) {
this.objectOfB = objectOfB;
}
public int add(int a, int b) {
int addition = objectOfB.performCalculation(a,b);
return addition;
}
}
允许 class 明确说明执行其设计功能所依赖的内容。
它还允许控制反转和松散耦合,这使得class更灵活地维护和测试
@RunWith(PowerMockRunner.class)
public class ClassATest {
@Test
public void testAddFromClassA() throws Exception {
int expected = 15;
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);
ClassA objA = new ClassA(objB);
//Act
int actual = objA.add(5, 10);
//Assert
assertEquals(expected, actual);
}
}
仅仅因为 PowerMockito 允许模拟构造新对象并不意味着我们应该这样做。
如果遵循正确的设计原则,那么就真的不需要这样的 hack。
我建议多阅读一些关于模拟及其实现方式的内容。
查看您尝试的测试代码 运行 :
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
int result = objA.add(5, 10);
assertEquals(result, 15);
这是怎么回事 .
您正在模拟 ClassB 但您没有使用模拟实例 objB
.
当你模拟一个 class 时,你会得到一个模拟的 instance ,这并不意味着 class 现在在你的测试中被模拟了,它只是意味着你可以使用 Mockito 来操作这个特定的实例。
应该怎么办 .
如果您不能使用该实例来测试依赖于它的方法,就像您的情况一样,这意味着您在这两个 classes 之间有很强的依赖性,如果这对您的测试来说是一个问题通常意味着你的设计有问题。您应该能够将 ClassA 注入到 ClassB 中,方法是在构造函数中为它提供一个参数
public ClassA(ClassB bInstance){
this.bIntance = bInstance
}
或者,如果这使得 since 对于特定操作,作为函数参数。
public add(ClassB classBInstance, int a, int b){
classBInstance. performCalculation(a.b)
}
这将允许您模拟实例和 运行 具有您模拟的特定实现的 ClassA 函数:
ClassB classBInstance1 = Mockito.mock(ClassB.class)
ClassB classBInstance2 = Mockito.mock(ClassB.class)
Mockito.when(classBInstance1.performCalculation(5, 10)).thenReturn(15);
Mockito.when(classBInstance2.performCalculation(5, 10)).thenReturn(42);
new ClassA(classBInstance1).add(5,10) //returns 15
new ClassA(classBInstance2).add(5,10) //returns 42
new ClassA().add(classBInstance1,5,10) //15
new ClassA().add(classBInstance2,5,10) //42
我正在为 class A 编写单元测试,我想模拟一个方法,但该方法是从 class 级别对象调用的良性方法,我将如何模拟它。
我举个例子解释一下
ClassA正在测试中
public class ClassA {
ClassB objectOfB = new ClassB();
public int add(int a, int b) {
int addition = objectOfB.performCalculation(a,b);
return addition;
}
}
Class B,有一定的业务逻辑。
public class ClassB {
public int performCalculation(int a, int b) {
int c = a+b;
System.out.println("I am not mocked, I am actual call");
System.out.println("Returning " + c + " From ClassB");
return c;
}
}
笔试
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class, ClassB.class})
public class ClassATest {
@InjectMocks
ClassA objA = new ClassA();
@Test
public void testAddFromClassA() throws Exception {
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
int result = objA.add(5, 10);
assertEquals(result, 15);
}
}
测试结果:
这个测试是通过的,但它不是模拟ClassB的方法,而是执行实际调用。
要求:
在编写测试时,我想模拟行:objectOfB.performCalculation(a,b);来自 class A,但如您所见,classB() 的对象是在 class 级别上创建的。
我如何模拟这个?
我应该在测试中写什么 class。
模拟 class 的初始化,以便在执行测试时使用模拟
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class}) //prepare the class creating the new instance of ClassB for test, not the ClassB itself.
public class ClassATest {
@Test
public void testAddFromClassA() throws Exception {
int expected = 15;
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);
//mocking initialization of ClassB class withing ClassA class
PowerMockito.whenNew(ClassB.class).withNoArguments().thenReturn(objB);
ClassA objA = new ClassA();
//Act
int actual = objA.add(5, 10);
//Assert
assertEquals(expected, actual);
}
}
引用How to mock construction of new objects
话虽如此,理想情况下目标 class 应该通过构造函数注入遵循显式依赖原则
public class ClassA {
final ClassB objectOfB;
public ClassA(ClassB objectOfB) {
this.objectOfB = objectOfB;
}
public int add(int a, int b) {
int addition = objectOfB.performCalculation(a,b);
return addition;
}
}
允许 class 明确说明执行其设计功能所依赖的内容。
它还允许控制反转和松散耦合,这使得class更灵活地维护和测试
@RunWith(PowerMockRunner.class)
public class ClassATest {
@Test
public void testAddFromClassA() throws Exception {
int expected = 15;
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(expected);
ClassA objA = new ClassA(objB);
//Act
int actual = objA.add(5, 10);
//Assert
assertEquals(expected, actual);
}
}
仅仅因为 PowerMockito 允许模拟构造新对象并不意味着我们应该这样做。
如果遵循正确的设计原则,那么就真的不需要这样的 hack。
我建议多阅读一些关于模拟及其实现方式的内容。 查看您尝试的测试代码 运行 :
ClassB objB = Mockito.mock(ClassB.class);
Mockito.when(objB.performCalculation(5, 10)).thenReturn(15);
int result = objA.add(5, 10);
assertEquals(result, 15);
这是怎么回事 .
您正在模拟 ClassB 但您没有使用模拟实例 objB
.
当你模拟一个 class 时,你会得到一个模拟的 instance ,这并不意味着 class 现在在你的测试中被模拟了,它只是意味着你可以使用 Mockito 来操作这个特定的实例。
应该怎么办 .
如果您不能使用该实例来测试依赖于它的方法,就像您的情况一样,这意味着您在这两个 classes 之间有很强的依赖性,如果这对您的测试来说是一个问题通常意味着你的设计有问题。您应该能够将 ClassA 注入到 ClassB 中,方法是在构造函数中为它提供一个参数
public ClassA(ClassB bInstance){
this.bIntance = bInstance
}
或者,如果这使得 since 对于特定操作,作为函数参数。
public add(ClassB classBInstance, int a, int b){
classBInstance. performCalculation(a.b)
}
这将允许您模拟实例和 运行 具有您模拟的特定实现的 ClassA 函数:
ClassB classBInstance1 = Mockito.mock(ClassB.class)
ClassB classBInstance2 = Mockito.mock(ClassB.class)
Mockito.when(classBInstance1.performCalculation(5, 10)).thenReturn(15);
Mockito.when(classBInstance2.performCalculation(5, 10)).thenReturn(42);
new ClassA(classBInstance1).add(5,10) //returns 15
new ClassA(classBInstance2).add(5,10) //returns 42
new ClassA().add(classBInstance1,5,10) //15
new ClassA().add(classBInstance2,5,10) //42