hotmeatballsoup
hotmeatballsoup

Reputation: 625

Wiring real dependencies into Spring Boot integration test

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!

Upvotes: 0

Views: 83

Answers (2)

seenukarthi
seenukarthi

Reputation: 8674

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.

Upvotes: 1

Feel free
Feel free

Reputation: 1069

You can try to add @RunWith(SpringRunner.class) @SpringBootTest to your class. I think spring does not see your injecting, so it might help.

And you can replace all your injects by

 @Autowired
 private PasswordService passwordService;

Upvotes: 1

Related Questions