Reputation: 115
I'm trying to run a unit test case but am having trouble getting reflection to work. My testSubject uses a supplier class to get a map. The supplier class has a modulusChecker autowired in that I'm trying to set with reflection. However, whenever I run the test the doubleAlterneteModulusChecker is set to 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;
}
}
Upvotes: 0
Views: 1172
Reputation: 669
Try to use autowiring via constructor instead of field.
public ModulusCheckerMapSupplier (DoubleAlternateModulusChecker doubleAlternateModulusChecker) {
this.doubleAlternateModulusChecker = doubleAlternateModulusChecker;
}
or using lombok annotation:
@Component
@RequiredArgsConstructor
public class ModulusCheckerMapSupplier implements Supplier<Map> {
private final DoubleAlternateModulusChecker doubleAlternateModulusChecker;
}
Upvotes: 1
Reputation: 18235
You mark @Spy
:
@Spy
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;
but not supply an initialization => Null.
If you want to spy on real bean instance, use spring @SpyBean
If you do not have access to that annotation, then use reflection:
@Autowired
private DoubleAlternateModulusChecker doubleAlternateModulusChecker;
@Autowired
private BankDetailsValidator testSubject; // Important, not @InjectMock
Then in your test:
DoubleAlternateModulusChecker spy = Mockito.spy(doubleAlternateModulusChecker);
ReflectionTestUtils.setField(modulusCheckerMapSupplier, "doubleAlternateModulusChecker", spy);
Upvotes: 1