Spring Boot integration testing here. I have the following service class:
@Service
public class PasswordService implements PasswordEncoder {
private BCryptPasswordEncoder passwordEncoder;
private PasswordGenerator passwordGenerator;
private CharacterCharacteristicsRule passwordCharacteristicsRule;
private Integer minPasswordLength;
@Autowired
public PasswordService(
BCryptPasswordEncoder passwordEncoder,
PasswordGenerator passwordGenerator,
CharacterCharacteristicsRule passwordCharacteristicsRule,
@Value("${myapp.users.passwords.min-length}") Integer minPasswordLength) {
this.passwordEncoder = passwordEncoder;
this.passwordGenerator = passwordGenerator;
this.passwordCharacteristicsRule = passwordCharacteristicsRule;
this.minPasswordLength = minPasswordLength;
}
public String generatePassword() {
return passwordGenerator.generatePassword(minPasswordLength, passwordCharacteristicsRule.getRules());
}
public String protectPassword(String plaintextPassword) {
return passwordEncoder.encode(plaintextPassword);
}
@Override
public String encode(CharSequence rawPassword) {
return protectPassword(rawPassword.toString());
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return protectPassword(rawPassword.toString()).equals(encodedPassword);
}
}
I have a unit test for each method, but because its generatePassword()
method is so important and depends so heavily on its dependencies, I also want to create an integration test for it that actually autowires/injects it with real instances of all of its dependencies.
My best attempt:
public class PasswordServiceIT {
@Inject
private BCryptPasswordEncoder passwordEncoder;
@Inject
private PasswordGenerator passwordGenerator;
@Inject
private CharacterCharacteristicsRule passwordCharacteristicsRule;
private PasswordService passwordService;
@BeforeEach
public void setup() {
passwordService = new PasswordService(passwordEncoder, passwordGenerator, passwordCharacteristicsRule, 10);
}
@Test
public void when_generatePassword_should_generate_valid_password() {
String password = passwordService.generatePassword();
System.out.println(String.format("Password is: %s", password));
}
}
Throws NPEs because passwordEncoder
and passwordGenerator
are being injected as null
. Again I'm looking for the real BCryptPasswordEncoder
and the real PasswordGenerator
and the real CharacterCharacteristicsRule
to be injected into my PasswordService
instance here, so I can take it for a real test spin.
Can anybody spot where I'm going awry here? Thanks in advance!
Do not create an instance for your service PasswordService
using the new
keyword. If you do, all the @Inject
/@Autowire
members will be null. You need to inject the PassworService
also.
And annotate the test class with org.springframework.boot.test.context.SpringBootTest
so when running the tests, a spring context will be created.
So you test class will as follows.
@SpringBootTest
public class PasswordServiceIT {
@Inject
private PasswordService passwordService;
@Test
public void when_generatePassword_should_generate_valid_password() {
String password = passwordService.generatePassword();
System.out.println(String.format("Password is: %s", password));
}
}
You don't need to inject other classes unless you directly use it in your tests in this class.
Note: For this, you need org.springframework.boot:spring-boot-starter-test
should be in the test scope dependency.