Spring Boot 切面的一种的测试方法

假设我们当前有一个 StudentControllor ,该控制器中存一个 getNameById 方法。

@RestController

public class StudentController {

@GetMapping(“{id}”)

public Student getNameById(@PathVariable Long id) {

return new Student("测试姓名");

}

public static class Student {

private String name;
public Student(String name) {
  this.name = name;
}
public String getName() {
  return name;
}
public void setName(String name) {
  this.name = name;
}

}

}

在没有切面前,我们访问该方法将得到相应带有测试姓名的学生信息。

建立切面

现在,我们使用切面的方法在返回的名字后台追加一个 Yz 后缀。

@Aspect

@Component

public class AddYzAspect {

@AfterReturning(value = “execution(* club.yunzhi.smartcommunity.controller.StudentController.getNameById(…))”,

  returning = "student")

public void afterReturnName(StudentController.Student student) {

student.setName(student.getName() + "Yz");

}

}

测试

如果我们使用普通测试的方法来直接断言返回的姓名当然是可行的:

@SpringBootTest

class AddYzAspectTest {

@Autowired

StudentController studentController;

@Test

void afterReturnName() {

Assertions.assertEquals(studentController.getNameById(123L).getName(), "测试姓名Yz");

}

}

但往往切面中的逻辑并非这么简单,在实际的测试中其实我们也完成没有必要关心在切面中到底发生了什么(发生了什么应该在测试切面的方法中完成)。我们在此主要关心的是切面是否成功的被执行了,同时建立相应的断言,以防止在日后面的代码迭代过程中不小心使当前的切面失效。

MockBean

Spring Boot为我们提供了 MockBean 来直接 Mock 掉某个 Bean 。在测试切面是否成功执行时,我们并不关心 StudentController 中的 getNameById() 方法的执行逻辑,所以适用于合适 MockBean 来声明。

@SpringBootTest

class AddYzAspectTest {

  • @Autowired
  • @MockBean

StudentController studentController;

MockBean 并不适合于测试切面,这是由于 MockBean 在生成新的代理时将直接忽略掉相关切面的注解,导致切面直接失效。

同时 MockBean 虽然可以用于来模拟 Controller ,但如果用它来模拟Aspect则会发生错误。

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration’: BeanPostProcessor before instantiation of bean failed;

MockSpy

除了 MockBean 以外,Spring Boot还准备了携带了真正的 Bean ,但该 Bean 又可以随时按需求 Mock 掉的,同时使用该注解生成的 Bean 并不会破坏原来的切面。

class AddYzAspectTest {

@SpyBean

StudentController studentController;

@SpyBean

AddYzAspect addYzAspect;

但在这需要 注意 的 @SpyBean 虽然成功的生成了两个可以被 Mock 掉的 Bean ,但在执行相应的 Mock 方法时其对应的切面方法会自动调用一次。比如以下代码将自动调用 AddYzAspect 中的afterReturnName 方法。

@Test

void afterReturnName() {

StudentController.Student student = new StudentController.Student("test");
Mockito.doReturn(student).when(this.studentController).getNameById(123L); :point\_left: 

}

而此时由于被 Mock 掉的方法声明了返回值,所以Mockito则会使用 null 来做为返回值来访问AddYzAspect 中的 afterReturnName 方法。所以此时则会发生了个 NullPointerException 异常:

java.lang.NullPointerException

at club.yunzhi.smartcommunity.aspects.AddYzAspect.afterReturnName(AddYzAspect.java:14)

所以我们在Mock被切的方法前,需要提前把切面的相关方法Mock掉,同时由于Mock被切方法时会以 null 来做为方法的返回值,所以在相应的参数上直接写入 null 即可:

要模拟Spring切面进行测试,可以使用Mockito框架来创建切面的Mock对象,然后在测试中使用这个Mock对象来验证切面的行为。 以下是一个简单的示例: 假设有一个名为MyAspect的切面,它有一个名为myAdvice的通知方法: ```java @Aspect public class MyAspect { @Before("execution(* com.example.service.*.*(..))") public void myAdvice() { // do something } } ``` 现在我们想要测试这个切面的行为,我们可以使用Mockito框架来创建一个MyAspect的Mock对象,并在测试中验证它的行为: ```java @RunWith(MockitoJUnitRunner.class) public class MyAspectTest { @Mock private JoinPoint joinPoint; @Mock private ProceedingJoinPoint proceedingJoinPoint; @InjectMocks private MyAspect myAspect; @Test public void testMyAdvice() throws Throwable { // create target object MyService myService = new MyService(); // create method signature MethodSignature signature = mock(MethodSignature.class); when(signature.getMethod()).thenReturn(myService.getClass().getMethod("doSomething")); // create join point joinPoint = mock(JoinPoint.class); when(joinPoint.getSignature()).thenReturn(signature); when(joinPoint.getTarget()).thenReturn(myService); // create proceeding join point proceedingJoinPoint = mock(ProceedingJoinPoint.class); when(proceedingJoinPoint.getSignature()).thenReturn(signature); when(proceedingJoinPoint.getTarget()).thenReturn(myService); when(proceedingJoinPoint.proceed()).thenReturn(null); // call advice myAspect.myAdvice(joinPoint); myAspect.myAdvice(proceedingJoinPoint); // verify behavior verify(joinPoint, times(2)).getSignature(); verify(joinPoint, times(2)).getTarget(); verify(proceedingJoinPoint, times(1)).proceed(); } } ``` 在这个测试中,我们使用Mockito创建了一个JoinPoint和ProceedingJoinPoint的Mock对象,并将它们注入到MyAspect的实例中。然后我们创建了一个MyService的实例作为目标对象,并创建了一个MethodSignature来模拟方法签名。 接下来,我们调用MyAspect的myAdvice方法,并使用创建的Mock对象来验证它的行为。在这个例子中,我们验证了getSignature和getTarget方法被调用了两次,并验证了proceedingJoinPoint.proceed方法被调用了一次。 这样,我们就可以使用Mockito框架来模拟Spring切面进行测试了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值