深度模拟与Mockito和Spring Boot

huangapple go评论56阅读模式
英文:

Deep mocking with Mockito and Spring Boot

问题

我需要使用Mockito模拟“B”类使用的“A”类,然而我的测试用例是针对“D”类完成的。

看看我的示例:

@Service
public class Aclass {
    public Long sumTwoNumbers(Long a, Long b){
       return a + b;
    }
}

@Service
public class Bclass {
     @Autowired    
     private  Aclass classA;

     public Long sunThreeNumbers(Long a, Long b, Long c){
           return classA.sumTwoNumbers(a, b) + c;
     }
}

@Service
public class Cclass {
     @Autowired    
     private  Bclass classB;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classB.sunThreeNumbers(a, b, c) + d;
     }
}

@Service
public class Dclass {
     @Autowired    
     private  Cclass classC;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classC.sunFourNumbers(a, b, c, d);
     }
}

我的测试是:

@ExtendWith(MockitoExtension.class)
public class DclassTest{

     @InjectMocks    
     private  Dclass classD;

     @Mock    
     private  Aclass classA; 
     /* 看,我需要模拟Aclass,而不是Bclass或Cclass。 */

     @Test
     public void testSunFourNumbers(){
        Mockito.when(classA.sumTwoNumbers(any(Long.class), any(Long.class))).thenReturn(0L);

        Long sum = classC.sunFourNumbers(1L, 1L, 1L, 1L);

        Assert.assertEquals(2L, sum);        
     } 
}

这只是我需要的简单示例。

当我运行这个测试时,我收到了Dclass中的NullPointerException。

这是可能的吗?

英文:

I need to mock (with Mockito) the "A" class used by "B" class however my test case is done to "D" class.

See my example:

@Service
public class Aclass {
    public Long sumTwoNumbers(Long a, Long b){
       return a + b;
    }
}

@Service
public class Bclass {
     @Autowired    
     private  Aclass classA;

     public Long sunThreeNumbers(Long a, Long b, Long c){
           return classA.sumTwoNumbers(a, b) + c;
     }
}

@Service
public class Cclass {
     @Autowired    
     private  Bclass classB;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classB.sunThreeNumbers(a, b, c) + d;
     }
}

@Service
public class Dclass {
     @Autowired    
     private  Cclass classC;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classC.sunFourNumbers(a, b, c, d);
     }
}

My test is :

@ExtendWith(MockitoExtension.class)
public class DclassTest{

     @InjectMocks    
     private  Dclass classD;

     @Mock    
     private  Aclass classA; 
     /* Look , I need to mock Aclass , not Bclass or Cclass. */
     
     @Test
     public void testSunFourNumbers(){
        Mockito.when(classA.sumTwoNumbers(any(Long.class), any(Long.class))).thenReturn(0L);

        Long sum = classC.sunFourNumbers(1L, 1L, 1L, 1L);

        Assert.assertEquals(2L, sum);        
     } 
}

It was a simple example of what I need.

When I run this I received NullPointerException in Dclass.

Is it possible ?

答案1

得分: 1

我认为你应该使用Mockito这样来测试你的Bclass类

public class BclassTest {

    Long a = 0L;
    Long b = 0L;
    Long c = 1L;

    @InjectMocks
    private Bclass bclass;

    @Mock
    private Aclass aclass;

    // 你应该在@Before方法中初始化你的Mock对象。
    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testSunThreeNumbers() {
        Mockito.when(aclass.sumTwoNumbers(anyLong(), anyLong())).thenReturn(0L);
        assertEquals(Long.valueOf(1), bclass.sunThreeNumbers(a, b, c));
    }
}
英文:

I Think You should Test Your class Bclass with Mockito Like this :

public class BclassTest {

        Long a = 0L;
        Long b = 0L;
        Long c = 1L;
         
         @InjectMocks
         private Bclass bclass;

         @Mock
         private Aclass aclass;
       
         //You Should initialize your Mock Objects in @Before method.
         @Before
         public void setup() {
         MockitoAnnotations.initMocks(this);

         @Test
         public void testSunThreeNumbers(){             
         Mockito.when(aclass.sumTwoNumbers(anyLong(),anyLong())).thenReturn(0L);
         assertEquals(Long.valueOf(1), bclass.sunThreeNumbers(Long a, Long b, Long c));
                }
         }
       }

答案2

得分: 1

根据示例代码,很明显以下服务的依赖关系如下:

  1. Dclass 依赖于 Cclass
  2. Cclass 依赖于 Bclass
  3. Bclass 依赖于 Aclass

当我们为 Dclass 编写单元测试案例(代码如下):

@Service
public class Dclass {
     @Autowired    
     private Cclass classC;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classC.sunFourNumbers(a, b, c, d);
     }
}

在这里,Dclass 直接依赖于 Cclass,因此在这种情况下,只需要模拟 Cclass。单元测试的目标是通过模拟其直接依赖项来测试小单元。

@InjectMocks Dclass classD; // 现在它有所有三个模拟对象(但不需要 A 和 B 的模拟)
@Mock Aclass classA;        // 不需要(Dclass 不依赖于 Aclass)
@Mock Bclass classB;        // 不需要(Dclass 不依赖于 Bclass)
@Mock Cclass classC;        // **必需** - 因为 Dclass 直接依赖于 Cclass

根据您的代码 - 您为 Aclass 创建了一个模拟对象并将其注入到了 Dclass 中
为 Aclass 模拟对象编写了 When(...).then(...)

@Test
public void testSunFourNumbers(){

    Mockito.when(classA.sumTwoNumbers(any(Long.class), any(Long.class))).thenReturn(0L);

    // 由于您的代码中没有针对 'classC' 的模拟对象,它将为 null,导致空指针异常
    Long sum = classC.sunFourNumbers(1L, 1L, 1L, 1L);

    Assert.assertEquals(2L, sum);        
}
英文:

As per the example code, its pretty clear that below services dependent as below

  1. DClass depends on Cclass
  2. Cclass on Bclass and
  3. Bclass on Aclass

When we write unit test cases for Dclass (code as below),

@Service
public class Dclass {
     @Autowired    
     private  Cclass classC;

     public Long sunFourNumbers(Long a, Long b, Long c, Long d){
           return classC.sunFourNumbers(a, b, c, d);
     }
}

Here Dclass is directly dependent on Cclass, so mocking Cclass is enough in this case. Unit testing is all about testing a small unit by mocking its direct dependents.

 @InjectMocks Dclass classD; // It has all 3 mocks now (but A and B mocks not needed)
 @Mock Aclass classA;        //not needed (Dclass not dependent on Aclass)
 @Mock Bclass classB;        //not needed (Dclass not dependent on Bclass)    
 @Mock Cclass classC;        // **Mandatory** - Because Dclass is directly dependent on Cclass
 
 As per your code - you have a mock for Aclass and injected mock into Dclass.
 When(...).then(...) written for Aclass mock.

 @Test
 public void testSunFourNumbers(){

    Mockito.when(classA.sumTwoNumbers(any(Long.class), any(Long.class))).thenReturn(0L);

    //Since you don't have mock for 'classC' in your code, it will go as null and result in nullpointer
    Long sum = classC.sunFourNumbers(1L, 1L, 1L, 1L);

    Assert.assertEquals(2L, sum);        
 }         

huangapple
  • 本文由 发表于 2020年8月15日 06:26:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/63420728.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定