有时,当我编写单元测试时,我应该模拟对超类的引用。
我读过这个问题: 问题
这个答案 用 DI 回答建议重构代码。但我做不到
如果超类方法足够大,则这个答案另一个答案不适合。就我而言,我有非常大的代码。是的,我知道这违反了 SOLID OOD 原则,但我应该编写测试。我没有足够的时间进行重构。
这个问题是4年前提出的!
目前Mockito或Powermock可以解决这个问题吗?
代码示例:
class BaseService {
public void save() {
// a lot of code here! I cannot change this code.
}
}
public Childservice extends BaseService {
public void save(){
//logic for testing
super.save();
//logic for testing
}
}
public class Parent {
public int save() {
return 99;
}
}
public class Child extends Parent {
public int save() {
int i = super.save();
return i*2;
}
}
并测试:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Parent.class)
public class ParentTest {
@Test
public void testSave() {
PowerMockito.suppress(PowerMockito.methodsDeclaredIn(Parent.class));
System.out.println(new Child().save());
}
}
输出:198
使用 Powermock,您可以替换或抑制方法,因此可以更改
BaseService.save()
完成的操作。您还可以创建不执行任何抑制操作的方法。您甚至可以抑制静态初始化块。
请阅读Powermock 作者的博客文章。请参阅“更换”一章。
更新:
抑制似乎对我有用,但替换不行。见下图:
这实际上并不是在嘲笑超级引用,但我认为这对于想要测试双亲方法的人很有帮助。
在我的例子中,我创建了一个新方法来包装超类的方法并进行测试替身。因为当我实现 oAuth2 客户端时,我需要来自超类方法的结果。我希望这个答案对需要使用超类方法结果的人有所帮助。
loadUserFromParent
方法来包装超类函数package action.in.blog.service;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DefaultOAuth2UserServiceDelegator extends DefaultOAuth2UserService {
private final List<CustomOAuth2UserService> oAuth2UserServices;
public DefaultOAuth2UserServiceDelegator(List<CustomOAuth2UserService> oAuth2UserServices) {
this.oAuth2UserServices = oAuth2UserServices;
}
public OAuth2User loadUserFromParent(OAuth2UserRequest userRequest) {
return super.loadUser(userRequest);
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
for (var oauth2Service : oAuth2UserServices) {
if (!oauth2Service.supports(userRequest)) {
continue;
}
var oauthUser = loadUserFromParent(userRequest);
return oauth2Service.createOrLoadUser(oauthUser);
}
throw new RuntimeException("Not found user service");
}
}
doReturn(expectedValue).when(testDouble).method(params)
package action.in.blog.service;
import action.in.blog.domain.CustomAuthentication;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
class DefaultOAuth2UserServiceDelegatorTest {
CustomOAuth2UserService googleUserService;
CustomOAuth2UserService facebookUserService;
DefaultOAuth2UserServiceDelegator sut;
@BeforeEach
void setUp() {
googleUserService = mock(CustomOAuth2UserService.class);
facebookUserService = mock(CustomOAuth2UserService.class);
sut = spy(new DefaultOAuth2UserServiceDelegator(
List.of(googleUserService, facebookUserService)
));
}
@Test
void loadUser() {
var authentication = new CustomAuthentication();
var oauth2UserRequest = mock(OAuth2UserRequest.class);
var oauth2User = new DefaultOAuth2User(Collections.emptyList(), Map.of("id", "junhyunny"), "id");
// make a stub
doReturn(oauth2User).when(sut).loadUserFromParent(oauth2UserRequest);
when(googleUserService.supports(oauth2UserRequest)).thenReturn(true);
when(googleUserService.createOrLoadUser(oauth2User)).thenReturn(authentication);
var result = sut.loadUser(oauth2UserRequest);
assertEquals(result, authentication);
}
}
这是不可能的;超类的全部要点在于它封装了上游状态和功能,并且类层次结构基于
extends
关系硬编码在子类中。