Data not being retrieved from embedded H2 datasource in springboot test - unit-testing

I've used this technique before and specifying all the configuration explicitly. The log indicates it's creating the datasource and loading the scripts:
o.s.j.d.e.EmbeddedDatabaseFactory - Starting embedded database: url='jdbc:h2:mem:balancer;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
o.s.j.d.i.ScriptUtils - Executing SQL script from class path resource [db/migration/V1_0001__create_queue_server_table.sql]
o.s.j.d.i.ScriptUtils - Executed SQL script from class path resource [db/migration/V1_0001__create_queue_server_table.sql] in 20 ms.
o.s.j.d.i.ScriptUtils - Executing SQL script from class path resource [db/migration/V1_0002__queue_server_entries.sql]
o.s.j.d.i.ScriptUtils - Executed SQL script from class path resource [db/migration/V1_0002__queue_server_entries.sql] in 8 ms.
o.s.o.j.LocalContainerEntityManagerFactoryBean - Building JPA container EntityManagerFactory for persistence unit 'default'
I am able to invoke the REST webservice and the call goes through to the Repository but nothing is returned. The application works fine when connecting to mysql and returns data that was loaded. I cannot see what is missing WRT configuration:
Testcase:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {QueueServiceApplication.class, TestConfig.class})
#WebAppConfiguration
#ActiveProfiles({"test"})
public class QueueServiceApplicationTests {
private static final int EXPECTED_SERVER_COUNT = 10;
#Autowired
private WebApplicationContext webCtx;
private MockMvc mvc;
#Before
public void init() {
mvc = MockMvcBuilders.webAppContextSetup(webCtx).build();
}
#Test
public void successListAll() throws Exception {
mvc.perform(get("/listall")).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$", hasSize(EXPECTED_SERVER_COUNT)));
}
}
Test configuration object for embedded datasource:
#Configuration
#Profile("test")
public class TestConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.addScript("classpath:/db/migration/V1_0001__create_queue_server_table.sql")
.addScript("classpath:/db/migration/V1_0002__queue_server_entries.sql")
.setType(EmbeddedDatabaseType.H2)
.setName("vitel-balancer")
.setScriptEncoding("UTF8")
.build();
}
}
Launcher:
#SpringBootApplication
public class QueueServiceApplication {
public static void main(String[] args) {
SpringApplication.run(QueueServiceApplication.class, args);
}
}
I'm using spring-boot 1.3.2, java 8 update 72

I have been able to address this by changing my TestConfig class
#Configuration
#Profile("test")
public class TestConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
#Bean
public ResourceDatabasePopulator populator() {
ResourceDatabasePopulator pop = new ResourceDatabasePopulator();
pop.addScript(new ClassPathResource("db/migration/V1_0001__create_queue_server_table.sql"));
pop.addScript(new ClassPathResource("db/migration/V1_0002__queue_server_entries.sql"));
pop.setContinueOnError(true);
return pop;
}
#Bean
public DataSourceInitializer dbInit(DataSource ds, ResourceDatabasePopulator populator) {
DataSourceInitializer init = new DataSourceInitializer();
init.setDataSource(ds);
init.setDatabasePopulator(populator);
init.setEnabled(true);
return init;
}
}
I'm not sure why this is different that using the EmbeddedDatabaseBuilder and adding the scripts to it. Would be greatful if there is an explaination

Related

How to inject a service into a constructor when creating a new instance of an object?

The Setup: I've registered a configuration service that pulls data from appsettings.json and it works fine. I also have a controller that uses that service to get settings from that file, again this works like it's supposed to:
public class ApiController : Controller
{
private readonly string _apiUri;
public ApiController(IOptions<Configurator> config)
{
_apiUri = config.Value.ApiSettings.ApiBaseUrl +
config.Value.ApiSettings.ApiVersion;
}
//...
}
Now note, I'm new to automated unit testing and to asp.net core. What I'd like to do is to decouple the ApiController's reliance on the injected service so that I can use a separate XUnit test project to test functions inside the controller, similar to the example in this tutorial.
To do this I created a model and interface representing the ApiSettings section of my appsettings.json file:
"ApiSettings": {
"ApiBaseUrl": "https://example.com/api/",
"ApiVersion": "v1/"
}
The Model:
public class ApiSettings : IApiSettings
{
public string ApiBaseUri { get; set; }
public string ApiVersion { get; set; }
}
The Interface:
public interface IApiSettings
{
string ApiBaseUri { get; set; }
string ApiVersion { get; set; }
}
I then created a class that would be dependent on the service to inject the settings:
public class ApiSettingsBuilder
{
private readonly string _apiUri;
public ApiSettingsBuilder(IOptions<Configurator> config)
{
_apiUri = config.Value.ApiSettings.ApiBaseUrl +
config.Value.ApiSettings.ApiVersion;
}
public string ApiUri { get { return _apiUri; } }
}
The Problem: How do I create an new instance of this class?
public class ApiController : Controller
{
private readonly string _apiUri;
public ApiController()
{
ApiSettingsBuilder builder = new ApiSettingsBuilder(/*What do I do here*/);
_apiUri = builder.ApiUri;
}
public ApiController(IApiSettings settings)
{
//For testing
_apiUri = settings.ApiBaseUrl + settings.ApiVersion;
}
//...
}
Also, I know this is all a bit overkill, but I would still like an answer because It would possibly be useful in other scenarios.
You don't have to create new classes for unit testing purposes, you can mock the interface of your IOptions using appropriate framework, e.g. Moq:
var configurator = new Configurator() { ApiBaseUrl = "abc" };
var mock = new Mock<IOptions<Configurator>>();
mock.Setup(ap => ap.Value).Returns(configurator);
Then you can pass mocked object to your constructor for unit testing:
var controller = new ApiController(mock.Object);

Connecting Spring boot to redis: NoClassDefFoundError: org/springframework/session/hazelcast/HazelcastFlushMode

So I have a spring boot app which I am trying to connect to a redis cluster hosted on AWS for the purposes of a session
Full error is:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'spring.session-org.springframework.boot.autoconfigure.session.SessionProperties': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.autoconfigure.session.SessionProperties]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/springframework/session/hazelcast/HazelcastFlushMode
In my build.gradle
buildscript {
ext {
springBootVersion = '1.5.9.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
group = 'haughton.daniel'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
ext {
springCloudVersion = 'Edgware.SR1'
}
processResources {
from ('.ebextensions/') {
into '.ebextensions'
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '2.0.0.RELEASE'
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity4', version: '2.1.2.RELEASE'
compile group: 'mysql', name: 'mysql-connector-java', version: '6.0.6'
compile group: 'org.springframework.session', name: 'spring-session-data-redis', version: '2.0.2.RELEASE'
compile('org.springframework.boot:spring-boot-starter-web')
compile ('org.apache.tomcat:tomcat-dbcp:8.0.30')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
In my app.properties
spring.session.store-type=redis
spring.redis.host=my aws redis end point
spring.redis.password=my password
spring.redis.port=6379
I am following the guide here https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot-redis.html#boot-how
My security config
#EnableWebSecurity
#Configuration
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
#Bean
public EvaluationContextExtension securityExtension() {
return new EvaluationContextExtensionSupport() {
#Override
public String getExtensionId() {
return "security";
}
#Override
public Object getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
};
}
}
#Configuration
#Order(2)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/web/login",
"/web/forgotPassword",
"/web/forgotPassword/useToken","/web/forgotPassword/**",
"/web/forgotPassword/useToken/**").permitAll().antMatchers("/web/**").
authenticated().
and()
.formLogin()
.loginPage("/web/login")
.permitAll()
.successHandler(loginSuccessHandler())
.failureUrl("/web/login-error")
.and()
.logout()
.logoutUrl("/web/logout")
.logoutSuccessUrl("/web/login")
.and().
csrf().disable();;
}
public AuthenticationSuccessHandler loginSuccessHandler() {
return (request, response, authentication) -> response.sendRedirect("/web");
}
#Bean
public EvaluationContextExtension securityExtension() {
return new EvaluationContextExtensionSupport() {
#Override
public String getExtensionId() {
return "security";
}
#Override
public Object getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {};
}
};
}
}
}
You are using incompatible versions of Spring Boot and Spring Session.
Spring Boot 1.5 is aligned with Spring Session 1.3, as can be seen in dependency versions appendix of Spring Boot's reference manual. OTOH, Spring Boot 2.0 is aligned with Spring Session 2.0, as can be seen here.
You should really avoid using explicit dependency version declarations in your build script, and rely on dependency management provided by Spring Boot, unless there's good reason to do otherwise.

How to mock HttpSession in JSF

i have a service method that get session attribute and i want to make unit test for this service method and i was wondering how to mock the HttpSession in jsf.
1- use the FacesContextMocker class:
public abstract class FacesContextMocker extends FacesContext {
private FacesContextMocker() {}
private static final Release RELEASE = new Release();
private static class Release implements Answer<Void> {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
setCurrentInstance(null);
return null;
}
}
public static FacesContext mockFacesContext() {
FacesContext context = Mockito.mock(FacesContext.class);
setCurrentInstance(context);
Mockito.doAnswer(RELEASE).when(context).release();
return context;
}
}
2- in the test class #Before method do the following:
FacesContextMocker.mockFacesContext();
ExternalContext externalContext = Mockito.mock(ExternalContext.class);
Mockito.when(FacesContext.getCurrentInstance().getExternalContext())
.thenReturn(externalContext);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(
FacesContext.getCurrentInstance().getExternalContext()
.getRequest()).thenReturn(request);
HttpSession httpSession = Mockito.mock(HttpSession.class);
Mockito.when(GeneralUtils.getHttpSession()).thenReturn(httpSession);
3- the getHttpSession method is as follows:
public static HttpSession getHttpSession() {
return ((HttpServletRequest) FacesContext.getCurrentInstance()
.getExternalContext().getRequest()).getSession();
}
4- in the test method do the following:
Mockito.when(
GeneralUtils.getHttpSession().getAttribute(
"userID")).thenReturn("1");
5- this is assuming that in your service method that you are making the unit test for you have code like:
String currentUserID = (String) GeneralUtils.getHttpSession()
.getAttribute(userID);

How can I test RESTful methods with Arquillian?

I have a set of classes to work with REST methods in project. They look like this:
#Path("customer/")
#RequestScoped
public class CustomerCollectionResource {
#EJB
private AppManager manager; // working with DB
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response list(#QueryParam("email") String email) {
final List<Customer> entities = manager.listCustomers(email);
// adding customers to result
return Response.ok(result).build();
}
}
After that I've wrote test method:
#RunWith(Arquillian.class)
public class CustomerResourceTest {
#Deployment
public static WebArchive createTestArchive() {
return ShrinkWrap.create(WebArchive.class, "test.war")
// Adding omitted
//.addClasses(....)
}
#Test #GET #Path("projectName/customer") #Consumes(MediaType.APPLICATION_JSON)
public void test(ClientResponse<List<Customer>> response) throws Exception {
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
}
And I get NullPointerException when trying to run this test. It's because of empty response in test case. Why is this happens? DB is configured properly.
There are two modes an arquillian test can run: in-container and client mode. HTTP interfaces can be tested only in client mode (never tried the extensions, only used vanilla Arquillian for this).
By default the test methods executed in the context of the container, called by the arquillian test runner servlet.
#RunWith(Arquillian.class)
public class CustomerResourceTest {
#EJB SomeBean bean; // EJBs can be injected, also CDI beans,
// PersistenceContext, etc
#Deployment
public static WebArchive createTestArchive() {
return ShrinkWrap.create(WebArchive.class, "test.war")
// Adding omitted
//.addClasses(....)
}
#Test
public void some_test() {
bean.checkSomething();
}
}
In client mode, the test methods are running outside of the container, so you don't have access to EJBs, EntityManager, etc injected into the test class, but you can inject an URL parameter for the test method.
#RunWith(Arquillian.class)
public class CustomerResourceTest {
// testable = false here means all the tests are running outside of the container
#Deployment(testable = false)
public static WebArchive createTestArchive() {
return ShrinkWrap.create(WebArchive.class, "test.war")
// Adding omitted
//.addClasses(....)
}
// baseURI is the applications baseURI.
#Test
public void create_account_validation_test (#ArquillianResource URL baseURI) {
}
You can use this URL parameter to build URLs to call your HTTP service using whatever method you have, like the new JAX-RS client API.
You can also mix the two modes:
#RunWith(Arquillian.class)
public class CustomerResourceTest {
#EJB SomeBean bean;
#Deployment
public static WebArchive createTestArchive() {
return ShrinkWrap.create(WebArchive.class, "test.war")
}
#Test
#InSequence(0)
public void some_test() {
bean.checkSomething();
}
#Test
#RunAsClient // <-- this makes the test method run in client mode
#InSequence(1)
public void test_from_client_side() {
}
}
This is sometimes even necessary, because some extensions, like persistence cannot run in client mode.

How to lookup a non-jta-data-source/jta-data-source via a Webservice

I get the following error when my Webservice is invoked:
org.apache.openjpa.persistence.ArgumentException: A JDBC Driver or DataSource class name must be specified in the ConnectionDriverName property.
The Webservice class is bascially trying to use JPA to insert data, and based on the above error message it seems the EntityManager cant access the datasource entry as shown below:
<persistence-unit name="TEST_P" transaction-type="RESOURCE_LOCAL">
<provider>
com.ibm.websphere.persistence.PersistenceProviderImpl
</provider>
<non-jta-data-source>java:comp/env/jdbc/DATA</non-jta-data-source>
<class>jpa.WSTGHandler</class>
<properties>
</properties>
</persistence-unit>
I have also defined the datasource entry in the web.xml as shown below:
<resource-ref>
<res-ref-name>java:comp/env/jdbc/DATA</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Embedding the JPA code in In Servlet suceesfully locates the datasource. how can I get the Webservice to work the same way ?
PS: I am using Websphere 7, JPA version 1.2, and JAX-WS
As requested, the follwing class is invoked int he Webservice method i.e beginTransaction()
public class EntityManagerHelper {
private static final EntityManagerFactory emf;
private static final ThreadLocal<EntityManager> threadLocal;
private static final Logger logger;
static {
emf = Persistence.createEntityManagerFactory("TEST_P");
threadLocal = new ThreadLocal<EntityManager>();
logger = Logger.getLogger("TEST_P");
logger.setLevel(Level.FINE);
}
public static EntityManager getEntityManager() {
EntityManager manager = threadLocal.get();
if (manager == null || !manager.isOpen()) {
manager = emf.createEntityManager();
threadLocal.set(manager);
}
return manager;
}
public static void closeEntityManager() {
EntityManager em = threadLocal.get();
threadLocal.set(null);
if (em != null) em.close();
}
public static void beginTransaction() {
getEntityManager().getTransaction().begin();
}
public static void commit() {
getEntityManager().getTransaction().commit();
}
public static void rollback() {
getEntityManager().getTransaction().rollback();
}
public static Query createQuery(String query) {
return getEntityManager().createQuery(query);
}
public static void log(String info, Level level, Throwable ex) {
logger.log(level, info, ex);
}
}
Issue resolved. it was just a typo in the res-ref-name :)