【问题标题】:How to mock a method which is being called from class level object如何模拟从类级别对象调用的方法
【发布时间】:2019-10-10 19:07:38
【问题描述】:

我正在为 A 类编写单元测试,我想模拟一个方法,但该方法是从类级别对象调用的良性方法,我将如何模拟它。

让我从例子中解释一下

正在测试的 A 类。

public class ClassA {
    ClassB objectOfB = new ClassB();
    public int add(int a, int b) {
        int addition = objectOfB.performCalculation(a,b);
        return addition;
    }
}

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);来自 A 类,但如您所见,classB() 的对象是在类级别上创建的。

我如何模拟这个?

我应该在我的测试课上写什么。

【问题讨论】:

    标签: java unit-testing mockito powermockito


    【解决方案1】:

    模拟类的初始化,以便在执行测试时使用模拟

    @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

    话虽如此,理想情况下,目标类应该通过构造函数注入遵循显式依赖原则

    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;
        }
    }
    

    允许类明确声明它依赖什么来执行其设计的功能。

    它还允许控制反转和松散耦合,这使得类的维护和测试更加灵活

    @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 允许模拟构造新对象并不意味着我们应该这样做。

    如果遵循正确的设计原则,那么就真的没有必要进行这样的黑客攻击了。

    【讨论】:

      【解决方案2】:

      我建议阅读更多关于模拟及其完成方式的内容。 查看您尝试运行的测试代码:

        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
      当您模拟一个类时,您会返回一个模拟的 instance ,这并不意味着该类现在在您的测试中被模拟了,它只是意味着您可以使用 Mockito 来操纵这个特定的实例。

      应该发生什么
      如果您不能使用该实例来测试一个方法依赖于它,就像在您的情况下,这意味着您在这两个类之间有非常强的依赖关系,如果这对您的测试来说是一个问题,通常意味着您的设计是有缺陷的。您应该能够将 ClassA 注入到 ClassB 中,方法是在构造函数中为其提供一个参数

      public ClassA(ClassB bInstance){
         this.bIntance = bInstance
      }
      

      或者,如果这对于特定操作来说是因为,作为函数参数。

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 2021-12-31
        相关资源
        最近更新 更多