Is it possible to do a constructor-based CDI injection of a #Resource type of instance?
I have the following class:
class MyClass {
#Resource
private ManagedExecutorService executorService;
#Inject
private MyService myservice;
}
I would like to convert it into something like this:
class MyClass {
private final ManagedExecutorService executorService;
private final MyService myservice;
#Inject
MyClass(ManagedExecutorService executorService, MyService myService)
{
this.executorService = executorService;
this.myService = myService;
}
}
This would make the class immutable and easier to unit test. The problem is that since the executorService needs to be obtained via a #Resource annotation, it doesn't seem to be injectable via the constructor.
Here is what I ended up doing - I created a producer class to managed the resource object:
public class ExecutorServiceProducer {
#Resource
private ManagedExecutorService managedExecutorService;
#Produces
#Managed
public ExecutorService createManagedExecutorService() {
return managedExecutorService;
}
}
and I created this custom annotation:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface Managed {
}
and then I was able to annotate my class as follows:
class MyClass {
private final ExecutorService executorService;
private final MyService myservice;
#Inject
MyClass(#Managed ExecutorService executorService, MyService myService)
{
this.executorService = executorService;
this.myService = myService;
}
}
This way I can unit test the class by providing my own ExecutorService (non-container managed) instance.
Related
I have an issue. I use Mockito Framework to test a service but I have to inject another service into it in order to work. The problem is that I get a NullPonterException when I try to use the second service. Is there a special way to inject the different mock objects into the two services. My code currently looks like this:
#ExtendWith(MockitoExtension.class)
public class Service1Test {
#Mock
Repository1 repository1;
#Mock
Repository2 repository2;
#Mock
Repository3 repository3;
#Mock
Repository4 repository4;
#InjectMocks
Service1 service1;
#InjectMocks
Service2 service2;
}
#Service
public class Service1Impl implements Service1 {
private final Repository1 repository1;
private final Repository2 repository2;
#Autowired
public Service1Impl (Repository1 repository1, Repository2 repository2) {
this.repository1; = repository1;
this.repository2; = repository2;
}
}
#Service
public class Service2Impl implements Service2 {
private final Repository1 repository1;
private final Repository2 repository2;
private final Repository3 repository3;
private final Repository4 repository4;
#Autowired
public PostServiceImpl(PostRepository postRepository, CommentRepository commentRepository, TagRepository tagRepository, CategoryRepository categoryRepository) {
this.postRepository = postRepository;
this.commentRepository = commentRepository;
this.tagRepository = tagRepository;
this.categoryRepository = categoryRepository;
}
}
I have following component:
#Component
public class ServiceManagerImpl implements ServiceManager {
private final ServiceA serviceA;
private final ServiceB serviceB;
private final String path;
#Autowired
protected ServiceManagerImpl(ServiceA serviceA, ServiceB serviceB, String path) {
this.serviceA= serviceA;
this.serviceB= serviceB;
this.path= path;
}
(...)
}
Now I want to create simple service to which I will inject above component with specific path value. This value should come from class with String constans:
#Component
public class ServiceManagerClientImpl implements ServiceManagerClient {
private ServiceManager serviceManager;
#Autowired
public ServiceManagerClientImpl(ServiceManager serviceManager) {
this.serviceManager = serviceManager;
}
}
Is it possible to dynamically inject simple path values on ServiceManagerClientImpl level (not from properties / yaml files)?
You can autowire the Environment class
import org.springframework.core.env.Environment;
#Autowired
private Environment environment;
You can read the enironment variable("path" in your case) using the below statement.
environment.getProperty("path");
Is it possible to just ignore/mock any injected dependencies inside a MockedBean?
Example:
#Service
public class MyService {
#Autowired
private MailerService mailer;
public void test1() {
//does not use mailer
}
public void test2() {
//...
mailer.send();
}
}
#Service
public class MailerService {
//I want these to be automatically mocked without explicit declaration
#Autowired
private JavaMailSender sender;
#Autowired
private SomeMoreService more;
//also these should be mocked without having to provide properties
#Value("${host}") private String host;
#Value("${user}") private String user;
#Value("${pass}") private String pass;
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class MyServiceTest {
#Autowird
private MyService myservice;
#MockBean
private MailserService mailer;
#Test
public void test1() {
myservice.test1();
}
}
I could use #MockBean to sort out mailer injection dependency. But any service inside the mocked bean would also have to be explicitly mocked.
Question: is it possible to mock a service "away". Means, just mock the bean and don't care what's inside the #MockedBean (or automatically also mock anything inside #MockedBean)?
As for me the best way to inject mocks is to use MockitoJUnitRunner
#RunWith(MockitoJUnitRunner.class)
public class MocksTests {
#InjectMocks
private ParentService parent;
#Mock
private InnerService inner; // this will be injected into parent
//your tests
}
#Component
public class IServiceCollection {
#Resource
private IService service1;
#Resource
private IService service2;
#Resource
private IService service3;
#Resource
private IService service4;
#Resource
private IService service5;
public List<IService> getAllServices(){
List<IService> iServiceList = new ArrayList<IService>();
iServiceList.add(service1);
iServiceList.add(service2);
return iServiceList;
}
}
in IServiceCollection I will refer lots of IService beans like service1, servvice2, etc. I wanna get all of the service beans in method getAllServices().
How can I add all the services to the list automatically, not like the code above?
You have a few options:
.1. If you inject in a map this way:
#Component
public class IServiceCollection {
#Autowired
private Map<String, IService> services;
that would inject in all implementations of IService with the key of the map being the bean name
.2. You can inject in a list this way:
#Component
public class IServiceCollection {
#Autowired
private List<IService> services;
again you would have a list of IService instances.
I was going through this tutorial which sets up the SUT using a constructor but my question is what if there is no constructor e.g. if we have:
#Autowire private PetRepository petRepository;
#Autowire private VetRepository vetRepository;
#Autowire private OwnerRepository ownerRepository;
#Autowire private VisitRepository visitRepository;
in the service/controller. How we can set this up?
I prefer to annotate the setters instead of the attributes on the classes. For example:
public class SomeClass implements SomeInterface {
private PetRepository petRepository;
private VetRepository vetRepository;
private OwnerRepository ownerRepository;
private VisitRepository visitRepository;
... some methods ...
#Resource
public void setPetRepository(PetRepository petRepository) {
this.petRepository= petRepository;
}
#Resource
public void setVetRepository(VetRepository vetRepository) {
this.petRepository= vetRepository;
}
#Resource
public void setOwnerRepository(OwnerRepository ownerRepository) {
this.ownerRepository = ownerRepository;
}
#Resource
public void setVisitRepository(VisitRepository visitRepository) {
this.visitRepository= visitRepository;
}
}
Then you can create a test case like this with Mockito and Junit:
public class SomeClassTestCase {
#Mock
private PetRepository petRepository;
#Mock
private VetRepository vetRepository;
#Mock
private OwnerRepository ownerRepository;
#Mock
private VisitRepository visitRepository;
private SomeClass someClass;
#Before
public void before(){
MockitoAnnotations.initMocks(this);
someClass = new SomeClass();
someClass.setPetRepository(petRepository);
someClass.setVetRepository(vetRepository);
someClass.setOwnerRepository(ownerRepository);
someClass.setVisitRepository(visitRepository);
}
#Test
public void someTest() {
...
}
}
Hope it helps.
You can use Mockito's #InjectMocks annotation. This will instantiate and then inject the Mock dependencies, for example:
#RunWith(MockitoJUnitRunner.class)
public ServiceTest {
#Mock
PetRepository petRepository
// ...omitted mocks ...
#InjectMocks
ClinicServiceImpl service;
}
See the documentation for further usage examples and caveats.