spring webclient unit test with mockito - rest

I'm trying to write a test for my controller (which has an autowired field pageModelService)
here is my test class:
#RunWith(MockitoJUnitRunner.class)
public class PageModelControllerTest {
#Mock
private PageModelService pageModelService;
#InjectMocks
private PageModelController pageModelController;
private WebTestClient client;
#Before
public void setup() {
client = WebTestClient.bindToController(pageModelController).build();
}
}
I can't create the client, it tells me it can't autowire pageModelService
any idea

Related

Integration Tests for RESTEasy Endpoint

I want to perform integration tests on my REST endpoint but am running into issues.
Below is my endpoint. NOTE: I cannot change this part of the code.
#Path("/people")
public class PersonResource {
private final PersonService personService;
#Inject
public PersonResource(final PersonService personService) {
this.personService = personService;
}
#GET
#Produces("application/json")
public List<Person> getPersonList() {
return personService.getPersonList();
}
}
From what I've been able to find online, I have the following basic structure for my test.
public class PersonResourceTest {
private Dispatcher dispatcher;
private POJOResourceFactory factory;
#Before
public void setup() {
dispatcher = MockDispatcherFactory.createDispatcher();
factory = new POJOResourceFactory(PersonResource.class);
dispatcher.getRegistry().addResourceFactory(factory);
}
#Test
public void testEndpoint() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("people");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
System.out.print("\n\n\n\n\n" + response.getStatus() + "\n\n\n\n\n");
System.out.print("\n\n\n\n\n" + response.getContentAsString() + "\n\n\n\n\n");
}
}
However, this results in the following error on the last line of the setup method.
java.lang.RuntimeException: RESTEASY003190: Could not find constructor for class: my.path.PersonResource
I explored the Registry API and thought maybe I should have been using addSingletonResource instead, so I changed the last line of setup to dispatcher.getRegistry().addSingletonResource(personResource); and added the following.
#Inject
private PersonResource personResource;
But that results in a NullPointerException on the last line of setup.
The sparse documentation on the mocking isn't very helpful. Can anyone point out where I'm going wrong? Thanks.
You need to do two things
Add a no arguments constructor to your source class:
public PersonResource() {
this(null)
}
In the test class, initialize the PersonResource class with an instance of PersonService class:
dispatcher.getRegistry().addSingletonResource(new PersonResource(new PersonService()));
If needed, the PersonService class can be mocked:
private Dispatcher dispatcher;
#Mock
private PersonService service;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
dispatcher = MockDispatcherFactory.createDispatcher();
PersonResource resource= new PersonResource(service);
ispatcher.getRegistry().addSingletonResource(resource);
}
Hope it helps!

Using embedded MongoDB in Spring JUnit #WebMvcTest

I'm currently using MongoDB in my Spring application. Since I added Mongo my endpoint tests no longer work due to the following error:
No qualifying bean of type 'xxx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
The repository which I Autowire in a controller is as follows:
private final RuleRepository ruleRepository;
#Autowired
public TestController(RuleRepository ruleRepository) {
this.ruleRepository = ruleRepository;
}
I assume that this has to do with Mongo and the fact that I currently use AutoConfiguration for it. For the test I added the Flapdoodle Embed Mongo dependency, which seems to be used for testing purposes in a lot of examples, to my pom.xml with the scope set to test:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>2.0.3</version>
<scope>test</scope>
</dependency>
My test class looks like this:
RunWith(SpringRunner.class)
#WebMvcTest(value = RouteController.class, secure = false)
#ActiveProfiles("test")
public class TestControllerEndpointTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private RuleRepository ruleRepository;
#Before
public void setupTests() {
//Setup for the tests
}
//Actual tests
}
I also created a Configuration class for the Mongo Test Database, but I don't know how to correctly register it:
#Configuration
#Profile("test")
public class TestMongoConfig {
#Autowired
private MongoProperties properties;
#Autowired(required = false)
private MongoClientOptions options;
#Bean(destroyMethod = "close")
public Mongo mongo(MongodProcess mongodProcess) throws IOException {
Net net = mongodProcess.getConfig().net();
return new MongoClient(net.getServerAddress().getHostName(), net.getPort());
}
#Bean(destroyMethod = "stop")
public MongodProcess mongodProcess(MongodExecutable mongodExecutable) throws IOException {
return mongodExecutable.start();
}
#Bean(destroyMethod = "stop")
public MongodExecutable mongodExecutable(MongodStarter mongodStarter, IMongodConfig iMongodConfig) throws IOException {
return mongodStarter.prepare(iMongodConfig);
}
#Bean
public IMongodConfig mongodConfig() throws IOException {
return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
}
#Bean
public MongodStarter mongodStarter() {
return MongodStarter.getDefaultInstance();
}
}
How do I get an endpoint test that is annotated with #WebMvcTest to use the embedded Mongo database?
After banging our heads for a while we found the #AutoConfigureDataMongo annotation.
import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
#RunWith(SpringRunner.class)
#WebMvcTest(value = SampleController.class, secure = false)
#AutoConfigureDataMongo
public class SampleControllerTest {
Just annotate your controller with it and you should see org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo logs when running this controller tests.

Injection of #PersistenceContext in CDI-Unit

Here is the unit testing code. When we run unit test code (SampleServiceTest2); EntityManager injected in AbstractDao is always null! How can we inject em during unit test.
*** SampleServiceTest2.java
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(CdiRunner.class)
public class SampleServiceTest2 {
#Inject SampleService greeter;
#Test
public void testGreeter() throws Exception {
System.out.println("before2");
greeter.addSampleData(new SampleDataDto(), new KullaniciDto());
System.out.println("after2");
}
}
*** SampleService.java
import javax.ejb.Stateless;
import javax.inject.Inject;
....
#Stateless
#SecuredBean
public class SampleService {
#Inject
SampleLogic sampleLogic;
#Yetki(tag="perm_add_sample_data")
public void addSampleData(SampleDataDto data, KullaniciDto aktifKullaniciDto){
SampleDataHelper sampleDataHelper = new SampleDataHelper();
SampleData sampleData = sampleDataHelper.getEntity(data);
KullaniciHelper kullaniciHelper = new KullaniciHelper();
Kullanici kullanici = kullaniciHelper.getEntity(aktifKullaniciDto);
sampleLogic.addData(sampleData, kullanici);
}
}
**** SampleLogic.java
import javax.inject.Inject;
....
public class SampleLogic {
#Inject
SampleDataDao sampleDataDao;
public void addData(SampleData data, Kullanici kullanici) {
addData1(data,kullanici);
System.out.println("SampleLogic : addData() called!");
}
public void addData1(SampleData data, Kullanici kullanici) {
sampleDataDao.create(data, kullanici);
}
}
**** SampleDataDao.java
public class SampleDataDao extends AbstractDao<SampleData> {
private static final long serialVersionUID = 1L;
}
**** AbstractDao.java
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
private static final long serialVersionUID = 1L;
#PersistenceContext(unitName="meopdb")
private EntityManager em;
protected EntityManager getEm() {
return em;
}
#SuppressWarnings("rawtypes")
private Class entityClass;
#SuppressWarnings("rawtypes")
private Class getEntityClass() {
if (entityClass == null) {
entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
return entityClass;
}
public T create(T t, Kullanici kullanici) {
if (t.getId() != null) {
throw new IllegalStateException("Create Operation: Oid should be null");
}
t.setId(getSeqNextValue(t));
t.setEklemeZamani(new Timestamp(Calendar.getInstance().getTimeInMillis()));
t.setEkleyenKullaniciId(kullanici.getId());
t.setDurumId(EnumDurum.AKTIF.getValue());
t = em.merge(t);
em.flush();
return t;
}
}
If you test with CDIUnit, the only thing you get is CDI injections, not the full power of Java EE. Injecting entityManager using #PersistenceContext into AbstractDAO is not part of standalone CDI, it is only supported when application is running within a Java EE application server.
The solution is to inject EntityManager using CDI mechanism and create a producer. The producer could be then switched for an alternative in unit tests to provide test entityManager. However, setting up JPA in a standalone unit test is not so straightforward, as you need to specify connection properties directly in persistence.xml file. Also, do not forget to add dependencies on a JPA implementation (hibernate, eclipselink) into your test dependencies.
However, if you do not want to adapt your application's code or you need more than CDI in your tests, you should have a look at Arquillian Java EE test framework.
Here is an example for CDIUnit:
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
...
#Inject
#Named("meopdb")
private EntityManager em;
...
}
// producer in application - just a wraper over `#PersisteneContext`
public class EntityManagerProducer {
#Produces
#PersistenceContext(unitName="meopdb")
#Named("meopdb")
private EntityManager em;
}
/* producer in your test sources - it creates entityManager via API calls instead of injecting via `#PersistenceContext`. Also, a different persistence unit is used so that it does not clash with main persistence unit, which requires datasource from app server
*/
public TestEntityManagerProducer {
#Produces
#ProducesAlternative // CDIUnit annotation to turn this on as an alternative automatically
#Named("meopdb")
public EntityManager getEm() {
return Persistence
.createEntityManagerFactory("meopdb-test")
.createEntityManager();
}
}
And it is not yet enough. You need to create a new persistence.xml in your test resources with the test persistence unit named "meopdb-test". For this unit you need to specify RESOURCE_LOCAL transaction-type, and specify connection information. And last thing not to forget - you need to list all your entities in the persistence.xml, or in external orm file. This is because your tests run outside of application server. Inside app server, JPA can find entities automatically.
As #OndroMih said, in CDI-Unit, the only thing you get is CDI injections. So you have to cheat a little.
You can use extension do add javax.inject.Inject annnotation to all #PersistenceContext injections
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import javax.persistence.PersistenceContext;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
public class AddInjectToPersistenceContextInjectionsCdiExtension implements Extension {
<T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> pat) {
Set<AnnotatedField<? super T>> fields = pat.getAnnotatedType().getFields();
for (AnnotatedField<? super T> field : fields) {
if (shouldInjectionAnnotationBeAddedToField(field)) {
AnnotatedType<T> at = pat.getAnnotatedType();
AnnotatedTypeBuilder<T> builder = new AnnotatedTypeBuilder<T>().readFromType(at);
Inject injectAnnotation = AnnotationInstanceProvider.of(Inject.class);
builder.addToField(field, injectAnnotation);
pat.setAnnotatedType(builder.create());
}
}
}
private <X> boolean shouldInjectionAnnotationBeAddedToField(AnnotatedField<? super X> field) {
return !field.isAnnotationPresent(Inject.class) &&
field.isAnnotationPresent(PersistenceContext.class);
}
}
and produce suitable EntityManager in test class
#RunWith(CdiRunner.class)
#AdditionalClasses(AddInjectToPersistenceContextInjectionsCdiExtension.class)
public class SampleServiceTest2 {
#Inject SampleService greeter;
EntityManagerFactory emf;
#PostConstruct
void init() {
emf = Persistence.createEntityManagerFactory("integration");
}
#Produces
EntityManager createEntityManager() {
return emf.createEntityManager();
}
#Test
public void testGreeter() throws Exception {
}
}
It's not exactly equivalent of what Java EE container does, but it's close enough more often than not.

When more than one tests added to rest controller test why am I getting WebApplicationContext is required?

This is very funny. When I ran my controller test with more than one tests I am getting the following error when i run it with maven, but works fine in eclipse Junit.java.lang.IllegalArgumentException: WebApplicationContext is required
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder.<init>(DefaultMockMvcBuilder.java:43)
at org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup(MockMvcBuilders.java:46)
at com.akrilist.rest.web.akripost.controller.AbstractRestControllerTest.setup(AbstractRestControllerTest.java:32)
at com.akrilist.rest.web.akripost.controller.AutoPostControllerTest.setup(AutoPostControllerTest.java:36) Then I ran one test commenting the other alternately (commented testA then run testB, then commented testB then run testA) both are passing. I have no idea what is happening when I put both of then are active tests. if any of you have clue please let me know. I have put my classes here.
AbstractRestControllerTest
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { TestRestServiceConfig.class, WebAppConfig.class })
#WebAppConfiguration
public abstract class AbstractRestControllerTest {
protected MockMvc mockMvc;
#Autowired
protected WebApplicationContext webApplicationContext;
/*#Inject
protected UserAccountService userAccountServiceMock;*/
#Before
public void setup() {
/* Mockito.reset(userAccountServiceMock);*/
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
AutoPostControllerTest
public class AutoPostControllerTest extends AbstractRestControllerTest {
#Autowired
private AutoPostService autoPostServiceMock;
#Autowired
private AutoPostConverter autoPostConverterMock;
#Before
public void setup() {
// Mockito.reset(autoPostServiceMock);
// Mockito.reset(commentPostRepositoryMock);
super.setup();
}
#Test
public void testValidationErrorForNullProfileId() throws Exception {
String description = TestUtil.createStringWithLength(501);
AutoPost autoPost = new TestAutoPostBuilder().description(description).buildModel();
mockMvc.perform(post("/auto/post").contentType(TestUtil.APPLICATION_JSON_UTF8).content(TestUtil.convertObjectToJsonBytes(autoPost))).andExpect(status().isBadRequest())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
// .andExpect(jsonPath("$[]", hasSize(1)))
.andExpect(jsonPath("$.type", is("validation failure")));
verifyZeroInteractions(autoPostServiceMock);
}
#Test
public void testGet_shouldReturnPost() throws Exception {
String description = TestUtil.createStringWithLength(501);
String postId = TestUtil.createStringWithLength(16);
Integer profileId = 123456;
TestAutoPostBuilder testAutoPostBuilder = new TestAutoPostBuilder();
AutoPost post = testAutoPostBuilder.postId(postId).description(description).profileId(profileId).buildModel();
when(autoPostServiceMock.get(postId)).thenReturn(post);
when(autoPostConverterMock.convertTo(post)).thenReturn(testAutoPostBuilder.buildDto());
mockMvc.perform(get("/auto/post/" + postId).contentType(TestUtil.APPLICATION_JSON_UTF8)).andExpect(status().isOk()).andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.postId", is(postId))).andExpect(jsonPath("$.profileId", is(profileId))).andExpect(jsonPath("$.links", hasSize(1)));
verify(autoPostServiceMock, times(1)).get(anyString());
verifyNoMoreInteractions(autoPostServiceMock);
}
}
I fixed this issue. It was because of parallel configuration of maven-surefire-plugin. I changed its value to 'classes', so the issue is over. There are two ways we can fix this issue. One is
<parallel>classes</parallel>
<threadCount>10</threadCount>
other way annotating the test class with #net.jcip.annotations.NotThreadSafe that required sequential execution.

Can I do setter injection using #Inject annotation

In my GWTP application I need to Inject HttpServletRequest, HttpSession as instance variable of ActionHandler.
My ActionHandler is initialized through Spring.
I can't get current Request object through Spring as it instantiates just POJO.
I am thinking about mixing GIN and Spring.
Would I be able inject HttpServletRequest using GIN in my ActionHandler which is instantiated through Spring?????
Is it possible to do following way??
#Configuration
#Import(DefaultModule.class)
public class ServerModule extends HandlerModule
{
#Bean
public UserVerficationActionHandler getUserVerificationActionActionHandler()
{
return new UserVerficationActionHandler();
}
}
public class UserVerficationActionHandler implements ActionHandler<UserVerficationAction, UserVerficationActionResult>
{
#Autowired
private UserService userService;
private Provider<HttpServletRequest> requestProvider;
#Inject
public UserVerficationActionHandler()
{
}
public UserVerficationActionResult execute(UserVerficationAction action, ExecutionContext context) throws ActionException
{
....
}
#Inject
public Provider<HttpServletRequest> setRequestProvider()
{
return requestProvider;
}
}
-------ActionHandler Ends--------
Can somebody let me know Is it possible to do SetterInjection this way?
Second thing, if above is possible then will I be getting current request object using this method?
Thanks in advance.
Bhavesh.