joesan
joesan

Reputation: 15385

Spring Boot Error When Running Unit Tests

After almost 8 years, I have to dust my Spring knowledge and things were good as long as I did not have to write unit tests. I have the following unit test that would test one of my service and when I tried to run it, it would fail with an:

org.springframework.beans.factory.UnsatisfiedDependencyException

and it is not able to resolve the eMailNotificationService service!

So here is my Unit test:

@ActiveProfiles("test")
@ComponentScan(basePackages = {"com.middleware.service.email", "it.ozimov.springboot.templating.mail"})
@RunWith(SpringRunner.class)
public class EMailNotificationServiceTest {

    @Autowired()
    private EMailNotificationService eMailNotificationService;

    @MockBean(name = "emailService")
    private EmailService emailService;

    @Test
    public void sendResetPasswordEMailNotification() {

        System.out.println(eMailNotificationService);

        // TODO: complete the test
    }
}

The EMailNotificationService is as below and this is defined in the com.middleware.service.email package:

@Service()
@Scope("singleton")
public class EMailNotificationService {

    private static Log logger = LogFactory.getLog(EMailNotificationService.class);

    @Value("${service.email.sender}")
    String senderEMail;

    @Value("${service.email.sender.name}")
    String senderName;

    @Value("${service.email.resetPassword.link}")
    String resetPasswordLink;

    @Autowired
    public EmailService emailService;

    public void sendResetPasswordEMail(List<EMailUser> userList) {
        List<String> allEMails = userList.stream()
                                    .map(EMailUser::getUserEMail)
                                    .collect(Collectors.toList());
        userList.stream().forEach(emailUser -> {
            final Email email;
            try {
                email = DefaultEmail.builder()
                        .from(new InternetAddress(senderEMail, senderName))
                        .to(Lists.newArrayList(new InternetAddress(emailUser.getUserEMail(), emailUser.getUserName())))
                        .subject("Reset Password")
                        .body("")//Empty body
                        .encoding(String.valueOf(Charset.forName("UTF-8"))).build();

                // Defining the model object for the given Freemarker template
                final Map<String, Object> modelObject = new HashMap<>();
                modelObject.put("name", emailUser.getUserName());
                modelObject.put("link", resetPasswordLink);

                emailService.send(email, "resetPasswordEMailTemplate.ftl", modelObject);
            } catch (UnsupportedEncodingException | CannotSendEmailException ex) {
                logger.error("error when sending reset password EMail to users " + allEMails, ex);
            }
        });
    }
}

How do I write my unit test such that my service is injected / autowired?

Upvotes: 1

Views: 2819

Answers (2)

dchrzascik
dchrzascik

Reputation: 341

You have used in your test a @MockBean annotation which comes with a spring boot test. Hence, if you are relying on features that spring-boot-test provides, you have to mark your test with a @SpringBootTest annotation (in which case you may also remove a @ComponentScan annotation). Consequently, spring will properly mock your dependencies (EmailService) and will provide you with EmailNotificationService bean.

Further information regarding spring boot testing that may be useful can be found in their reference documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

Update:

Such tests bring up the whole context which may be unnecessary and fit rather to integration tests rather than unit tests. For 'pure' unit tests, you can forget that you are testing spring services and treat your classes as POJO. In that case, you can use Mockito for mocking your dependencies (which btw comes with spring-boot-test). You can rewrite your code in the following manner:

@RunWith(MockitoJUnitRunner.class)
public class EMailNotificationServiceTest {

    @InjectMocks
    private EmailNotService eMailNotificationService;

    @Mock
    private EmailService emailService;

    @Before
    public void setup() {
        eMailNotificationService.setSenderEMail("myemail");
        // set other @Value fields
    }

    @Test
    public void sendResetPasswordEMailNotification() {
        System.out.println(eMailNotificationService);
        // TODO: complete the test
    }

}

Upvotes: 1

Sergii Getman
Sergii Getman

Reputation: 4371

I prefer use this constructions:

public static void manuallyInject(String fieldName, Object instance, Object targetObject)
        throws NoSuchFieldException, IllegalAccessException {
    Field field = instance.getClass().getDeclaredField(fieldName);
    field.setAccessible(true);
    field.set(instance, targetObject);
}

in your case : manuallyInject("eMailNotificationService", emailService, eMailNotificationService)

Upvotes: 1

Related Questions