Java 让反射在单元测试中工作的问题

Java 让反射在单元测试中工作的问题,java,spring,mockito,junit5,Java,Spring,Mockito,Junit5,我试图运行一个单元测试用例,但在让反射正常工作时遇到了问题。我的testSubject使用一个supplier类来获取地图。supplier类有一个modulesChecker,我试图用反射设置它。但是,每当我运行测试时,doubleAlterneteModulusChecker都设置为null package uk.co.cdl.account.bankdetailsvalidation.api.impl; import static org.junit.jupiter.api.Assert

我试图运行一个单元测试用例,但在让反射正常工作时遇到了问题。我的testSubject使用一个supplier类来获取地图。supplier类有一个modulesChecker,我试图用反射设置它。但是,每当我运行测试时,doubleAlterneteModulusChecker都设置为null

package uk.co.cdl.account.bankdetailsvalidation.api.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.ReflectionTestUtils;

import uk.co.cdl.account.bankdetailsvalidation.model.BankDetailsValidationModel;
import uk.co.cdl.account.bankdetailsvalidation.moduluschecker.DoubleAlternateModulusChecker;
import uk.co.cdl.account.bankdetailsvalidation.supplier.ModulusCheckerMapSupplier;
import uk.co.cdl.account.bankdetailsvalidation.supplier.ModulusWeightTableSupplier;
import uk.co.cdl.account.bankdetailsvalidation.supplier.SortCodeSubstitutionTableSupplier;

@ExtendWith(MockitoExtension.class)
@DisplayName("CDLBankDetailsValidator Unit Tests")
class CDLBankDetailsValidatorTest {

    @Spy
    private DoubleAlternateModulusChecker doubleAlternateModulusChecker;

    @Spy
    private ModulusCheckerMapSupplier modulusCheckerMapSupplier;

    @Spy
    private ValidationExceptionRuleFactory validationExceptionRuleFactory;

    @Spy
    private SortCodeSubstitutionTableSupplier sortCodeSubstitutionTableSupplier;

    @Spy
    private ModulusWeightTableSupplier mockModulusTableSupplier;

    @InjectMocks
    private BankDetailsValidator testSubject;

    @DisplayName("validate() returns a BankDetailsValidationModel with the correct values for a given bank account number / sort code")
    @ParameterizedTest(name = "{3}" )
    @MethodSource("bankAccountDetailsTestData")
    void testCDLValidatorWithTestData(String sortCode, String accountNumber, BankDetailsValidationModel expected, String testDescription){
        ReflectionTestUtils.setField(modulusCheckerMapSupplier, "doubleAlternateModulusChecker", doubleAlternateModulusChecker);

        //then
        assertEquals(expected, testSubject.validate(accountNumber, sortCode));
    }

    @DisplayName("Logger captures the correct debug messages")
    @ParameterizedTest(name="Logged: {2}")
    @MethodSource("loggerTestData")
    void testLoggerCapturesDebugMessages(String sortCode, String accountNumber, String expected, int index){
        //Given
        ListAppender<ILoggingEvent> listAppender = getListAppender();

        //When
        testSubject.validate(accountNumber, sortCode);
        String actual = listAppender.list.get(index).toString();

        //Then
        assertEquals(expected, actual);
    }

    private ListAppender<ILoggingEvent> getListAppender(){
        Logger logger = (Logger) LoggerFactory.getLogger(CDLBankDetailsValidator.class);
        ListAppender<ILoggingEvent>listAppender = new ListAppender<>();
        listAppender.start();
        logger.addAppender(listAppender);
        return listAppender;
    }
}

@Component
public class ModulusCheckerMapSupplier implements Supplier<Map> {

    @Autowired
    private DoubleAlternateModulusChecker doubleAlternateModulusChecker;

    @Override
    public Map get() {
        Map<String, StandardModulusChecker> modulusChecksByAlgorithmName = new HashMap<>();
        modulusChecksByAlgorithmName.put(DBLAL, doubleAlternateModulusChecker);
        modulusChecksByAlgorithmName.put(MOD10, new StandardModulusChecker(10));
        modulusChecksByAlgorithmName.put(MOD11, new StandardModulusChecker(11));
        return modulusChecksByAlgorithmName;
    }
}

@Component
public class DoubleAlternateModulusChecker extends StandardModulusChecker {
    private static Logger LOGGER = LogManager.getLogger(DoubleAlternateModulusChecker.class);

    @Override
    public String getName() {
        return "Double alternate modulus check";
    }

    @Override
    public int getRemainder(String data, int[] weights) {
        return 1;
    }
}

包uk.co.cdl.account.bankdetailsvalidation.api.impl;
导入静态org.junit.jupiter.api.Assertions.assertEquals;
导入java.util.array;
导入java.util.Collection;
导入ch.qos.logback.classic.Logger;
导入ch.qos.logback.classic.spi.ILoggingEvent;
导入ch.qos.logback.core.read.ListAppender;
导入org.junit.jupiter.api.DisplayName;
导入org.junit.jupiter.api.extension.extensedWith;
导入org.junit.jupiter.params.ParameterizeTest;
导入org.junit.jupiter.params.provider.MethodSource;
导入org.mockito.InjectMocks;
导入org.mockito.Spy;
导入org.mockito.junit.jupiter.MockitoExtension;
导入org.slf4j.LoggerFactory;
导入org.springframework.test.util.ReflectionTestUtils;
导入uk.co.cdl.account.bankdetailsvalidation.model.BankDetailsValidationModel;
import uk.co.cdl.account.bankdetailsvalidation.moduluschecker.DoubleAlternateModulusChecker;
import uk.co.cdl.account.bankDetails验证.supplier.modulesCheckerMapSupplier;
import uk.co.cdl.account.bankDetails验证.supplier.modulesWeightTableSupplier;
import uk.co.cdl.account.BankDetails验证.supplier.SortCodeSubstitutionTableSupplier;
@ExtendWith(MockitoExtension.class)
@DisplayName(“CDLBankDetailsValidator单元测试”)
类CDLBankDetailsValidatorTest{
@间谍
私人DoubleAlternateModulusChecker DoubleAlternateModulusChecker;
@间谍
私有模块CheckerMap供应商模块CheckerMap供应商;
@间谍
私有ValidationExceptionRuleFactory ValidationExceptionRuleFactory;
@间谍
私有SortCodeSubstitutionTableSupplier SortCodeSubstitutionTableSupplier;
@间谍
私有模块权重表供应商mockmoduleustablesupplier;
@注射模拟
私人银行详情验证人测试对象;
@DisplayName(“validate()返回一个BankDetailsValidationModel,其中包含给定银行帐号/排序代码的正确值”)
@参数化测试(name=“{3}”)
@MethodSource(“bankAccountDetailsTestData”)
void testCDLValidatorWithTestData(字符串sortCode、字符串accountNumber、预期的银行详细信息验证模型、字符串testDescription){
ReflectionTestUtils.setField(ModulesCheckerMapSupplier,“DoubleAlternateModulsChecker”,DoubleAlternateModulsChecker);
//然后
assertEquals(预期,testSubject.validate(accountNumber,sortCode));
}
@DisplayName(“记录器捕获正确的调试消息”)
@参数化测试(name=“Logged:{2}”)
@MethodSource(“loggerTestData”)
void testLoggerCapturesDebugMessages(字符串sortCode、字符串accountNumber、所需字符串、int索引){
//给定
ListAppender ListAppender=getListAppender();
//什么时候
testSubject.validate(accountNumber,sortCode);
String actual=listAppender.list.get(index.toString();
//然后
资产质量(预期、实际);
}
私有ListAppender getListAppender(){
Logger Logger=(Logger)LoggerFactory.getLogger(CDLBankDetailsValidator.class);
ListAppenderlistAppender=新的ListAppender();
listpappender.start();
logger.addAppender(listpappender);
返回列表追加器;
}
}
@组成部分
公共类模块CheckMapSupplier实现供应商{
@自动连线
私人DoubleAlternateModulusChecker DoubleAlternateModulusChecker;
@凌驾
公共地图获取(){
Map ModuleUschecksbyAlgorithmName=new HashMap();
modulesChecksByAlgorithmName.put(DBLAL,doubleAlternateModulusChecker);
ModulusCheckByAlgorithmName.put(MOD10,新标准ModulusChecker(10));
ModulusCheckByAlgorithmName.put(MOD11,新标准ModulusChecker(11));
返回modulesCheckByAlgorithmName;
}
}
@组成部分
公共类DoubleAlternateModulusChecker扩展了StandardModulusChecker{
私有静态记录器Logger=LogManager.getLogger(DoubleAlternateModulusChecker.class);
@凌驾
公共字符串getName(){
返回“双交替模数检查”;
}
@凌驾
public int getrements(字符串数据,int[]权重){
返回1;
}
}

您标记了
@Spy

@Spy
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;
但不提供初始化=>Null

如果您想监视真实的bean实例,请使用spring

如果您无权访问该注释,请使用反射:

@Autowired
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;

@Autowired
private BankDetailsValidator testSubject;  // Important, not @InjectMock

然后在你的测试中:

DoubleAlternateModulusChecker spy = Mockito.spy(doubleAlternateModulusChecker);
ReflectionTestUtils.setField(modulusCheckerMapSupplier, "doubleAlternateModulusChecker", spy);


您标记了
@Spy

@Spy
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;
但不提供初始化=>Null

如果您想监视真实的bean实例,请使用spring

如果您无权访问该注释,请使用反射:

@Autowired
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;

@Autowired
private BankDetailsValidator testSubject;  // Important, not @InjectMock

然后在你的测试中:

DoubleAlternateModulusChecker spy = Mockito.spy(doubleAlternateModulusChecker);
ReflectionTestUtils.setField(modulusCheckerMapSupplier, "doubleAlternateModulusChecker", spy);


尝试通过构造函数而不是字段使用自动连线

public ModulusCheckerMapSupplier (DoubleAlternateModulusChecker doubleAlternateModulusChecker) {
   this.doubleAlternateModulusChecker = doubleAlternateModulusChecker;
}
或使用lombok注释:

@Component
@RequiredArgsConstructor
public class ModulusCheckerMapSupplier implements Supplier<Map> {
   private final DoubleAlternateModulusChecker doubleAlternateModulusChecker;
}
@组件
@所需参数构造函数
公共类模块CheckMapSupplier实现供应商{
私人最终DoubleAlternateModulusChecker DoubleAlternateModulusChecker;
}

尝试通过构造函数而不是字段使用自动布线

public ModulusCheckerMapSupplier (DoubleAlternateModulusChecker doubleAlternateModulusChecker) {
   this.doubleAlternateModulusChecker = doubleAlternateModulusChecker;
}
或使用lombok注释:

@Component
@RequiredArgsConstructor
public class ModulusCheckerMapSupplier implements Supplier<Map> {
   private final DoubleAlternateModulusChecker doubleAlternateModulusChecker;
}
@组件
@所需参数构造函数
公共类模块CheckMapSupplier实现供应商{
私人最终DoubleAlternateModulusChecker DoubleAlternateModulusChecker;
}

考虑添加完整版本的单元测试,包括所有相关注释。您使用什么
Runner
/
扩展来运行测试?完成,我正在使用MockitoExtension。
testSubject.validate
实际做什么?我假设您所面临的问题只针对 TestCDLValualActhTestDATa<代码>,因为只有在那里您试图通过反射设置字段。考虑添加单元测试的完整版本,包括所有相关注释。