I am trying to test a Java class that has a member injected using #Inject.
I want to test the someMethod
public class SomeJavaClass {
#Inject
private SomeFactory someFactory;
private SomeDAO someMethod(Map someMap) {
SomeDAO someDAO = someFactory.create();
//some code
}
}
My class does not have a explicit constructor
If your field is private, and you don't use constructors, how do you inject it? I will assume that this was a mistake, because if you inject something from the outside, you need to provide an interface for it.
So here's a spock spec that does what you ask, but exposes someFactory as a protected member:
import spock.lang.Specification
import spock.lang.Subject
import javax.inject.Inject
interface SomeFactory {
SomeDAO create()
}
class SomeDAO {
}
class SomeJavaClass {
#Inject
protected SomeFactory someFactory;
protected SomeDAO someMethod(Map someMap) {
SomeDAO someDAO = someFactory.create();
//some code
}
}
class SomeJavaClassSpec extends Specification {
def someFactory = Mock(SomeFactory)
#Subject
SomeJavaClass subject = new SomeJavaClass(
someFactory: someFactory
)
def "It uses a factory to create some object"() {
when:
subject.someMethod([:])
then:
1 * someFactory.create() >> new SomeDAO()
}
}
You can also use the spock collaborators extension annotations to inject your collaborator Mocks automatically:
https://github.com/marcingrzejszczak/spock-subjects-collaborators-extension
Of course you can inject to private fields (Guice and Spring can do that). You can use my extension but it would be much better if you added the constructor since then it follows the OOP practices.
Related
I have an abstract service class which autowires a specified type of repository.
abstract class SomeService<T extends SomeRepository<U>, U ...> {
#Autowrired
#Accessors(fluent = true)
#Getter(PROTECTED)
private U repositoryInstance;
}
Now I'm trying to create an abstract test class for subclasses of the service class.
#SpringBootTest
abstract class SomeServiceTest<T extends SomeService<U>, U extends SomeRepository<V>, V ...> {
#Autowrired
#Accessors(fluent = true)
#Getter(PROTECTED)
private T serviceInstance;
// DOES NOT WORK!
#MockBean
#Accessors(fluent = true)
#Getter(PROTECTED)
private U repositoryInstance; // != serviceInstance.repositoryInstance();
}
But mocking the bean in a test class of actual service class works.
class OtherServiceTest
extends SomeServiceTest<OtherService, OtherRepository, ...> {
#TestConfiguration
OtherServiceTestConfiguration {
// WORKS!!!
// == serviceInstance().repositoryInstance();
#MockBean private OtherRepository repositoryInstance;
}
}
class AnotherServiceTest
extends SomeServiceTest<AnotherService, AnotherRepository, ...> {
// WORKS!!!
// == serviceInstance().repositoryInstance();
#MockBean private AnotherRepository repositoryInstance;
}
How can I mock the SomeServiceTest#repositoryInstance so that it refers the same object as SomeServiceTest#serviceInstance.repositoryInstance()?
I don't know what is #Autowire, because I use #Autowired. But, as far I know, use autowire in the object declaration is a bad practice.
Instead of:
#Autowired
MyObject myObject;
Use it in the constructor:
Repository repository;
#Autowired
public SomeService(Repository repository) {
this.repository = repository;
}
And, in your test:
class SomeServiceTest {
#Mock
private Repository repositoryMock;
private Service serviceUnderTest;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
serviceUnderTest = new Service(repositoryMock);
}
You can always declare a default and an all-args constructor. The DI framework will instantiate via the first one, injecting the autowired fields, whereas you can instantiate with the all-args one from your test class, passing mock objects as constructor arguments.
I'm using weld-junit to using CDI in my tests.
How do I inject a instance of a Class that I created before? Like I can do in Binder that extends from AbstractBinder.
My Test Class:
#EnableWeld
public class VersionProcessTest {
#WeldSetup
public WeldInitiator weld = WeldInitiator
.of(VersionProcess.class);
#Test
public void GetSolversVersion() throws DirectoryNotFoundException {
weld.event().select(FileService.class).fire(new FileService("workDir", "solverTest"));
String version = weld.select(VersionProcess.class).get().execute();
assertThat("should return version string", version, instanceOf(String.class));
}
}
My Binder:
public class TestBinder extends AbstractBinder {
#Override
protected void configure() {
bind(new FileService("test")).to(FileService.class);
bind(SolverVersionApiServiceImpl.class).to(SolverVersionApiService.class);
}
}
So what I really looking for was to declarate a Produces for my Injection Class.
For that I add the Produces declaration for FileService (injected class):
#RequestScoped
#Produces
FileService fileService = new FileService("workDir", "solverTest");
And change the WeldInitiator declaration to set the class that contains the #Produces annotation, in my case was the TestClass, so:
#WeldSetup
public WeldInitiator weld = WeldInitiator.from(VersionProcess.class, VersionProcessTest.class).activate(RequestScoped.class).build();
And voilá it's working the dependency injection for VersionProcess class.
Here I am trying to mock autowire fields ServiceHelper of Service class TestServiceImpl , I am not able to call method through mock object of ServiceHelper class.
This is my class files:
#Service
public class TestServiceImpl implements TestService {
#Autowired
private TestDAO testDAO;
#Autowired
private ServiceHelper serviceHelper;
#Override
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
return serviceHelper.getResults(map);
}
2nd Class:
#Repository
public class ServiceHelper {
private static Logger logger = Logger.getLogger(ServiceHelper.class.getName());
#Autowired
ResponseHeader responseHeader;
public void setResponseHeader(ResponseHeader responseHeader) {
this.responseHeader = responseHeader;
}
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
....
}
And Test class:
#RunWith(MockitoJUnitRunner.class)
public class MockitoTester {
#InjectMocks
private TestServiceImpl serviceImpl = new TestServiceImpl();
#Mock
private TestDAO testDAO;
#Mock
private ServiceHelper sHelper;
#Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void testResult() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("TestId", "test123");
map.put("lang", "en");
map.put("cntry", "USA");
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
Assert.assertEquals(results.getStatus(), "Success");
}
Here in my test class:
results = serviceImpl.getResults(map);
It goes to TestServiceImpl class to method :
public ResultsModel getResults(Map<String, Object> map) throws webServiceException_Exception {
return serviceHelper.getResults(map);
}
but at point :
serviceHelper.getResults(map);
it is not going inside serviceHelper.getResults(map) and return all values as Null.
Please suggest where I need to do changes.
You have three choices here:
Do actual Spring autowiring in your tests
Use injection methods that can legitimately be performed by your tests (constructor parameters, public setters, public fields - in order of preference)
Use reflection to inject your mocks
Option 1 is really integration testing -- you can annotate your test class with #RunWith(SpringRunner.class) and use more Spring annotations to control dependency injection. It's too big a subject to cover in a SO answer, but there are plenty of examples if you Google for "spring integration test mockito".
But for unit testing, I think it's better not to involve Spring. A good Spring bean doesn't need Spring to function. Option 2 just says, write your class so that unit tests (and anything else) can inject the dependency (be it a mock, or anything else) through normal Java means.
Constructor injection is cleanest in my opinion:
private final ServiceHelper serviceHelper; // note: not annotated
#Autowired
public TestService(ServiceHelper serviceHelper) {
this.serviceHelper = serviceHelper;
}
But you can also do this with a public void setServiceHelper(ServiceHelper helper) -- this is less good because the field can't be final.
Or by making the field public -- I assume you know the reasons this is bad.
If you're determined to have a private field that's not set by a public constructor or setter, you could use Spring's ReflectionUtils.setField() from within your test:
#Mock
private ServiceHelper serviceHelper;
private TestService service;
#Before
public void configureService() {
service = new TestService();
Field field = ReflectionUtils.findField(TestService.class, "serviceHelper");
ReflectionUtils.setField(field, service, serviceHelper);
}
(Or, equally, use JDK's reflection classes directly, or reflection utils from elsewhere)
This is explicitly using reflection to subvert the access rules you've coded into the class. I thoroughly recommend option 2.
I think the issue may be that you are stubbing your method to return the same object which you then assign the result of the method under test. i.e. (the results object here):
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
This will probably cause some sort of cyclic confusion when it tries to stub the method in Mockito, and it certainly won't make your assertation pass:
Assert.assertEquals(results.getStatus(), "Success");
Since the status on results is never set anywhere.
I think you need to make separate objects for your stubbing and your returned value from the method under test and make sure you set your stubbed one to have a status of "Success":
ResultsModel results = new ResultsModel();
results.setStatus("Success");
when(sHelper.getResults(map)).thenReturn(results);
ResultsModel returnedResults = serviceImpl.getResults(map);
Assert.assertEquals(returnedResults.getStatus(), "Success");
Try using constructor injection it'd be easier to mock the classes for testing... here's an example on how I would structure my classes to get you going. When you write your tests you now have to pass the Mocked object into the instance you're creating of these classes:
#Service
public class TestServiceImpl implements TestService {
private TestDao testDao;
private ServiceHelper serviceHelper;
#Autowired
public TestServiceImpl(TestDAO testDAO, ServiceHelper serviceHelper) {
this.testDAO = testDAO;
this.serviceHelper = serviceHelper;
}
}
#Repository
public class ServiceHelper {
private ResponseHeader responseHeader;
#Autowired
public ServiceHelper(ResponseHeader responseHeader) {
this.responseHeader = responseHeader
}
}
I have to create a instance of a class, that have autowired elements, for test.
public class MyClass extends SomeOtherClass {
#Autowired
public MyClass(OtherClass1 one, OtherClass2 two){
super(one, two)
}
}
How can i in code create instance of this class, with the arguments wired in though spring?
Your test can be made Spring-aware if you use the SpringJUnit4ClassRunner to read in your Spring Context to be used in the test. For instance:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"the-config.xml"})
public final class MyClassTests {
#Autowired
private MyClass testee;
#Test
public void testSomething() {
assertThat(testee).doesSomethingExpected();
}
}
Note that you should reuse as much of your production config as possible and not create a parallel Spring Context config that mirrors it.
Instead of passing the other elements in as constructor arguments, you Autowire them as properties. Spring will then inject the objects.
public class MyClass extends SomeOtherClass {
#Autowired
private OtherClass1 one;
#Autowired
private OtherClass2 two
public MyClass(){
super(one, two)
}
}
Edit: Based on http://www.mkyong.com/spring/spring-auto-wiring-beans-with-autowired-annotation/, adding #Autowired to the constructor is also valid.
If you want to Autowire MyClass, you must annotate it with #Component or a similar annotation such as #Service.
#Component
public class MyClass extends SomeOtherClass
Then, you can use it in other classes
public class ClassThatUsesMyClass {
#Autowire
private MyClass myClass;
}
I am mocking an abstract class like below:
myAbstractClass = Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS);
the problem is that MyAbstractClass has some dependencies injected via EJB annotations and there are not setters. Is there a way to inject the dependencies?
#InjectMocks does not work with Abstract classes.
I am using junit5 for this.
What I did is instantiate the abstract class with a new abstractClass() in #BeforeEach and call the methods by super if the method is not abstract(using this because I have protected methods), after this I use ReflectionUtils.setField() to set the mocks in the abstract class and test every method and works pretty well. I leave a simple example that works.
AbstractClass
public abstract class AbstractClass {
#Autowired
private Environment environment;
protected String getProperty(String property){
return environment.getRequiredProperty(property);
}
}
AbstractClassTest
#ExtendWith(MockitoExtension.class)
class AbstractClassTest {
AbstractClass abstractClass;
#Mock
Environment environment;
#BeforeEach
void setUp() {
abstractClass = new AbstractClass() {
#Override
public String getProperty(String property) {
return super.getProperty(property);
}
};
ReflectionTestUtils.setField(abstractClass, "environment", environment);
}
#Test
void shouldReturnProperty() {
String propertyValue = "this property";
when(environment.getRequiredProperty("property")).thenReturn(propertyValue);
String property = abstractClass.getProperty("property");
assertEquals(propertyValue, property);
}
}
This is just using mockito and junit5 to test.
Remember to call ReflectionUtils after you instantiate the class with new AbstractClass() or the mocks won't be injected.
Any improve on this implementation is welcome :D.
Since you cannot instantiate an Abstract class there is nothing to test. I would recommend that you create child class (it could be a nested class inside your test class), and then run your tests that way. Then you can use the #Mock, #InjectMocks as you would normally.
You can use the Powermock library to inject mocks in myAbstractClass using Whitebox.setInternalState(myAbstractClass, mock(MockedClass.class));
Junit 4 specific solution
Abstract class that need to be tested
#Slf4j
public abstract class AdhocNotificationEmail {
#Autowired
protected CustomerNotificationRepository customerNotificationRepository;
protected abstract Map<String, String> abstractMethod(AdhocNotificationDTO adhocNotificationDTO);
public JSONObject concreteMethod(){
// some stuff that needs to be tested and common to all subclasses
}
}
Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
public class AdhocNotificationEmailTest{
#Mock
protected CustomerNotificationRepository customerNotificationRepository;
private AdhocNotificationEmail unit;
#Before
public void setUp() {
unit = new AdhocNotificationEmail() {
#Override
protected Map<String, String> abstractMethod(AdhocNotificationDTO notificationDTO) {
return null;
}
};
unit.customerNotificationRepository = customerNotificationRepository;
}
#Test
public void concreteMethod_greenPath() {
final String templateName = "NOTIFICATION_TEMPLATE";
final AdhocNotificationDTO adhocNotificationDTOStub = getAdhocNotificationDTOStub(templateName);
final CustomerNotification customerNotificationStub = getCustomerNotificationStub(templateName);
when(customerNotificationRepository.findByIdAndTemplateName(id, templateName)).thenReturn(customerNotificationStub);
final JSONObject response = unit.concreteMethod(adhocNotificationDTOStub);
assertNotNull(response);
}
Personally, what I like to do is to extend the abstract with an anonymous class declared in the #Before (or #BeforeEach in junit.jupiter). This way I can achieve the following:
Don't mock the class that I want to test (like you are doing with Mockito.mock(MyAbstractClass.class, Mockito.CALLS_REAL_METHODS)), since that is kind of an anti-pattern. You only want to Mock the dependencies of the class you are testing;
You can provide Mocks for the dependencies of the abstract class, and that are called by non-abstract methods;
You can test non-abstract methods that call abstract methods
Example:
class TestAbstractClass {
#Mock
private ServiceDependency dependency;
private AbstractClass abstractClass;
#BeforeEach
void setUp() {
abstractClass= new AbstractClass (dependency) { };
}
#Test
void test(){
Mockito.when(dependency.someMethod()).thenReturn(something);
var result = abstractclass.someNonAbstractMethod();
// assertions
}
}
Good practice is to write unit tests for all possible classes which inherits from this abstract class. Because in theory it is possible situation. This dependency should be mocked and you should forget about mocking dependencies of this EJB component.
Maybe some code snippets would help to clarify what you try to achieve here.