1

The class that I want to test is called UserService with sendEmail method, which sends an email to user.

To accomplish this task it depends on EmailService. Now when writing a testcase to test this - should I create UserService userService = new UserService() and mock Email service OR create context file define UserService bean there and @Autowired UserService in my test class and mock EmailService?

What is the difference between both approaches and when should i use one over the other? Which of these is a real object?

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
tech_ques
  • 1
  • 4
  • 1
    [This discussion](https://stackoverflow.com/questions/40620000/spring-autowire-on-properties-vs-constructor) about using @Autowired vs constructor injection might help. Treat unit tests the same as production code and keep them as lean as possible. In this case, the second option makes the unit test Spring aware which, IMO, makes the unit test more complex than it needs to be. – Andrew S Aug 01 '18 at 13:37

1 Answers1

-1

If your class is meant to be a bean, and for example you also have the expectation to get dependencies injected, you should not create instances with the new operator. Please check inject-doesnt-work-with-new-operator answer

You can create a TestConfig.class and mock the dependencies of the UserService (using a mock framework that you like - I prefer mockito). In this TestConfig, you create your bean:

@Configuration
 public static class TestConfig {

   @Bean
   private EmailService emailService() {
       return Mockito.mock(EmailService.class);
   }

   //Assuming that you have constructor injection.
   @Bean
   public UserService userService() {
     return new UserService(emailService());
   }
}
Eirini Graonidou
  • 1,506
  • 16
  • 24
  • If you want a simple unit test it is perfectly fine to construct your own instance and to not rely on spring doing all the wiring. Ideally you should be doing both (one is the unit test the other is more or less the integration test). – M. Deinum Aug 01 '18 at 14:05
  • @M.Deinum If there is a chance that a Bean is also being used as a POJO class outside of the spring framework. Could you please give me a concrete example where this is perfectly fine, please? Also, if you create an instance of the class you want to test, without letting spring to do the wiring, it is not an integration test anymore? Thanks in advance. – Eirini Graonidou Aug 01 '18 at 14:15
  • Where did I say it was an integration test? I even said that it then was a unit test and not an integration test). – M. Deinum Aug 02 '18 at 05:33
  • I wouldn't like to turn this into a discussion, but I would really like to understand. (Please correct me if I'm wrong) I think you implied, that letting spring do the dependencies wiring would be a case of an integration test, as you wrote that "Ideally you should be doing both (one is the unit test the other is more or less the integration test)." It would also be very nice of you, if you answered my follow up question: Why should we need to unit test a class once as a POJO and once as a bean? Is there any real example that you could provide? – Eirini Graonidou Aug 02 '18 at 07:06
  • If you have some complex business logic you first want to make sure that that works in isolation (the unit). If you don't your test might get clouded by setup issues and wrong configuration. Not to mentioned that a unit test is, generally, a lot faster then a full blown integration test. Next you want an integration test (or rather a system test). You send a request and test everything from the front to back in a single call. Check if messages get send, persisted in db etc. etc. – M. Deinum Aug 02 '18 at 07:41
  • I understand the difference between a unit- and an integration test. What I don't get is how it is different, if you let spring inject your mocked dependencies than injecting them yourself. From my point of view in both cases you have a unit test.. Plus, I see no reason doing both (as you recommended) because either your class is being used as bean or as a POJO. What I can take, is that the test is becoming a little bit slower. But as @Andrew S suggested, it is better to treat unit tests as production code. – Eirini Graonidou Aug 02 '18 at 08:02
  • Because letting Spring inject the dependencies isn't just letting spring inject the dependencies. The application context (especially when adding Spring Boot in the mix) does a whole lot more then just inject the dependencies. You want to prove that your service works without the risk of a failing test due to some other borked dependency of configuration in spring. – M. Deinum Aug 02 '18 at 08:04
  • And this also applies, if you define a TestConfig and run the test with some relative Runner let's say MockitoJUnitRunner? If yes, please confirm so that I can edit or delete my answer. You could post one, too. I find it oxymoron for an accepted answer to have minus one points. Thanks in advance. – Eirini Graonidou Aug 02 '18 at 08:54
  • How would a `MockitoJUnitRunner` load a test based configuration (unless you load it yourself). Also creating custom configurations isn't really testing your real configuration, but rather a cobbled together configuration to make the test work. If you want an integration test you should use the real configuration (imho that is), with some small modifications. But we can agree to disagre. The topic on what is a unit, integration, system test is cause for fierce discussions and opinions. Regarding the downvote, the OP should up-vote the answer and then it will go away. – M. Deinum Aug 02 '18 at 09:17