As we known, it's very easy to write down only rabbitMQ integration test with testcontainer.
But when I also introduce mysql integration test with testcontainer.
I found they have some conflict. When I only run IT of rabbitMQ, error occurrs for no DB to Mysql. and I need to add #Container for mysql, or else mysql can not find a container to access.
Currently I think of two solution:
exclude the database connection or something like JPA test ?
move mysql IT and MQ IT together to one class, so that will create the two containers
For 1. I don't know how to use the #annotation
For 2. I think it's not elegant
rabbitMQ Integration Test:
package com.example.myapp
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.util.TestPropertyValues
import org.springframework.http.MediaType
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.testcontainers.containers.RabbitMQContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
#SpringBootTest
#AutoConfigureMockMvc
//#ContextConfiguration(initializers = [RabbitControllerTest.Initializer::class])
#Testcontainers(disabledWithoutDocker = true)
//class RabbitControllerTest : BaseIntegrationTest() {
//#DataJpaTest(
// excludeAutoConfiguration =
// [
// DataSourceAutoConfiguration::class,
// DataSourceTransactionManagerAutoConfiguration::class,
// HibernateJpaAutoConfiguration::class
// ]
//)
//#AutoConfigureMockMvc
//#RunWith(SpringRunner.class)
//#ContextConfiguration(classes = [DemoApplication::class])
// #WebMvcTest(MyController.class)
class RabbitControllerTest {
#Autowired
lateinit var mockMvc: MockMvc
#Autowired
lateinit var objectMapper: ObjectMapper
companion object {
#Container
private val rabbitMQContainer = KRabbitMQContainer(image = "rabbitmq:3.7-management").withExposedPorts(5672)
// .withExposedPorts(5673)
.withVhost("/").withUser("admin", "admin").withPermission("/", "admin", ".*", ".*", ".*")
// .withStartupTimeout(Duration.ofSeconds(10))
// #Container
// private val mysqlContainer = KMySQLContainer(image = "mysql:8.0.22")
// .withDatabaseName("myapp")
#JvmStatic
#DynamicPropertySource
fun properties(registry: DynamicPropertyRegistry) {
// registry.add("spring.datasource.url", mysqlContainer::getJdbcUrl)
// registry.add("spring.datasource.password", mysqlContainer::getPassword)
// registry.add("spring.datasource.username", mysqlContainer::getUsername)
// registry.add("spring.jpa.hibernate.ddl-auto") { "create-drop" }
// rabbitMQ
registry.add("spring.rabbitmq.host") { rabbitMQContainer.host }
registry.add("spring.rabbitmq.port") { rabbitMQContainer.amqpPort }
// registry.add("spring.rabbitmq.port") { 5673 }
// registry.add("spring.rabbitmq.username", rabbitMQContainer::getAdminUsername)
// registry.add("spring.rabbitmq.password", rabbitMQContainer::getAdminPassword)
}
}
#Test
fun `should rabbitMQ send out message`() {
val json = objectMapper.writeValueAsString(TodoRequest("Write a blog on Testcontainers"))
this.mockMvc.perform(
post("/set-foo").contentType(MediaType.APPLICATION_JSON).content(json)
).andDo(print()).andExpect(status().isOk)
}
}
class KRabbitMQContainer(val image: String) : RabbitMQContainer(image)
controller using rabbitTemplate:
package com.example.myapp.rabbitmq
import org.springframework.amqp.rabbit.core.RabbitTemplate
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
#RestController
class ExampleController {
#Autowired
private lateinit var rabbitTemplate: RabbitTemplate
#PostMapping("/set-foo")
fun setFoo(#RequestBody value: String) {
rabbitTemplate.convertAndSend(MessagingConfig.FANOUT_EXCHANGE_NAME, "", value)
}
}
application.properties:
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}/myapp
build.gradle.kts:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.7.4"
id("io.spring.dependency-management") version "1.0.14.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
extra["testcontainersVersion"] = "1.17.4"
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
runtimeOnly("mysql:mysql-connector-java")
testImplementation("org.springframework.boot:spring-boot-starter-test")
// testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:mysql")
// // testImplementation("org.testcontainers:testcontainers:1.17.5")
testImplementation("org.awaitility:awaitility:4.2.0")
testImplementation("org.testcontainers:rabbitmq:1.17.5")
testImplementation("org.testcontainers:junit-jupiter:1.17.5")
implementation("org.springframework.boot:spring-boot-starter-amqp")
testImplementation("com.rabbitmq:amqp-client:5.14.2")
testImplementation("org.springframework.amqp:spring-rabbit-test:2.4.3")
testImplementation("org.springframework.amqp:spring-rabbit-junit:2.4.3")
}
dependencyManagement {
imports {
mavenBom("org.testcontainers:testcontainers-bom:${property("testcontainersVersion")}")
}
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
plz notice that :
** implementation("org.springframework.boot:spring-boot-starter-data-jpa") ** will automaticlly connect to database, if I comment out this, IT will not try to connect with database.
I have comment out the mysql container creation, if you revert back, the test will run successfully.
Thanks hope hear your precious voice
Related
I'm using fusedlocationclient to get the current user location, but somehow the lasLocation is null.
Here's my Location Fragment:
package com.example.atry.MakeComplaint
import Retrofit.INodeJS
import Retrofit.Observables
import Retrofit.RetrofitClient
import android.app.Activity
import android.content.ContentValues.TAG
import android.content.Context
import android.content.Context.*
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.*
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.gms.maps.MapView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import com.ashiqur.weatherapp.utils.GPSUtils
import com.example.atry.R
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.dynamic.SupportFragmentWrapper
import com.google.android.gms.location.*
import com.google.android.gms.maps.*
import com.google.android.gms.maps.model.*
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_complaint_details.view.*
import kotlinx.android.synthetic.main.fragment_location.*
import kotlinx.android.synthetic.main.fragment_location.view.*
import retrofit2.Call
import retrofit2.Response
import java.io.IOException
import java.util.Observer
import java.util.Optional.empty
import java.util.jar.Manifest
import javax.security.auth.callback.Callback
class LocationFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
override fun onMarkerClick(p0: Marker?)= false
private lateinit var map: GoogleMap
private lateinit var mapView : MapView
lateinit var myAPI: INodeJS
var MyCategory: Observables.ComplaintType?=null
private var listener: OnLocationFragmentInteractionListener? = null
var objectComplaint =
Observables.Complaint(
1 , "dummy problem" ,
"url" ,
Observables.Location("99","99"),
Observables.ComplaintType("Smell", "null"),
Observables.Status(2 , "Unresolved")
)
//for updating user's location/ for current location
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationRequest: LocationRequest
private lateinit var locationCallback: LocationCallback
private lateinit var lastLocation: Location
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val permissions = arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION)
requestPermissions( permissions,0)
getLocationUpdates()
//INIT API
val retrofit = RetrofitClient.instanc
myAPI = retrofit.create(INodeJS::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val v = inflater.inflate(com.example.atry.R.layout.fragment_location, container, false)
mapView = v.findViewById(R.id.maps)
mapView.onCreate(savedInstanceState)
mapView.onResume()
try {
MapsInitializer.initialize(getActivity()!!.getApplicationContext());
} catch (sendEx: IntentSender.SendIntentException) {
sendEx.printStackTrace();
}
mapView.getMapAsync(this)
v.backToList.setOnClickListener {
backFragment()
}
v.forwardToDescription.setOnClickListener{
getAllData()
}
return v
}
/**
* call this method in onCreate
* onLocationResult call when location is changed
*/
private fun getLocationUpdates() {
//with fusedLocationClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
locationRequest = LocationRequest()
locationRequest.interval = 1000
locationRequest.fastestInterval = 5000
locationRequest.smallestDisplacement = 170f // 170 m = 0.1 mile
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY //set according to your app function
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
if (locationResult.locations.isNotEmpty()) {
// get latest location
lastLocation = locationResult.lastLocation
Log.d("lastlocation",lastLocation.toString())
// use your location object
// get latitude , longitude and other info from this
}
}
}
}
//Places the marker on the map and changes its style.
//start location updates
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
null /* Looper */
)
}
// stop location updates
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
// start receiving location update when activity visible/foreground
override fun onResume() {
super.onResume()
mapView.onResume()
startLocationUpdates()
}
// stop receiving location update when activity not visible/foreground
override fun onPause() {
super.onPause()
mapView.onPause()
stopLocationUpdates()
}
override fun onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
override public fun onLowMemory() {
super.onLowMemory();
mapView.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap?) {
map = googleMap!!
map.uiSettings?.isZoomControlsEnabled = true
map.isMyLocationEnabled = true
val marker = MarkerOptions().position(LatLng(lastLocation.latitude,lastLocation.longitude)).title("hello maps")
marker.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
map.addMarker(marker)
val cameraPosition = CameraPosition.builder().target(LatLng(lastLocation.latitude,lastLocation.longitude)).zoom(12f).build()
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
private fun backFragment() {
val manager = (context as AppCompatActivity).supportFragmentManager
manager.popBackStackImmediate()
}
}
Also, if someone could just get this working by using location manager or something else I would really appreciate it. Or maybe a link or video where I could follow.
I've tried to get some json from this api: https://api.cointelegraph.com/api/v1/mobile/feed
I want to get the "title" field of each news. I created the class that supposed to get this fields and wrapper for this class, but I'm getting an empty list instead of this, i guess it's because I have to get field, which called "data" firstly and then parse it. So I changed my class for getting "title" fields to class for getting "data" field, but I'm still getting empty list.
I guess it's kinda stupid mistake, because it works with github api. I will put some kind of response below and my code. Thanks for trying to help.
Json response:
{"data":{"news":[{"type":"header","data":{"id":22358,"title":"MIT, Stanford Researchers to Fund New \u2018Globally Scalable\u2019 Cryptocurrency, \u2018Unit-e\u2019","lead":"Major U.S. universities including MIT, Stanford and UC Berkeley have teamed up to fund a new digital currency.","thumb":"https://s3.cointelegraph.com/storage/uploads/view/f7a7d42a1cba645f861ba810d2524f77.jpg","published_at":{"date":"2019-01-17 18:52:00.000000","timezone_type":3,"timezone":"Europe/London"},"share_url":"https://cointelegraph.com/news/mit-stanford-researchers-to-fund-new-globally-scalable-cryptocurrency-unit-e","views":2375,"type":"news","author":"Helen Partz","author_id":545,"tag":"Blockchain","badge":{"title":"News","label":"default"},"isSponsored":false,"audio":"https://s3.cointelegraph.com/audio/22358.71732d08-dc28-4787-98b1-46c4e0317916.mp3"}},{"type":"currencies","data":[{"price":3677.37,"name":"BTC","currency":"USD","isIncreased":1,"difference":"0.89"},{"price":123.57,"name":"ETH","currency":"USD","isIncreased":1,"difference":"0.17"},{"price":31.56,"name":"LTC","currency":"USD","isIncreased":1,"difference":"0.19"},{"price":0.33,"name":"XRP","currency":"USD","isIncreased":1,"difference":"1.22"}]}
Interface:
package com.hfad.cointask.service
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.NewsList
import retrofit2.Call
import retrofit2.http.*
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call<NewsList>
}
News class:
package com.hfad.cointask.model
class News (private var data: String) {
fun getData(): String{
return data
}
}
I've tried to get title in first time like that
package com.hfad.cointask.model
class News (private var title: String) {
fun getTitle(): String{
return title
}
}
My wrapper for News class:
package com.hfad.cointask.model
class NewsList {
var list = mutableListOf<News>()
}
My adapter:
package com.hfad.cointask.adapter
import android.support.v7.view.menu.MenuView
import android.support.v7.view.menu.MenuView.ItemView
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.hfad.cointask.R
import com.hfad.cointask.model.News
class CoinAdapter(var values: List<News>): RecyclerView.Adapter<CoinAdapter.CoinViewHolder>() {
override fun onBindViewHolder(p0: CoinViewHolder, p1: Int) {
val itemTitle: News = values[p1]
p0.newsTitle.text = itemTitle.getData()
}
override fun getItemCount(): Int {
return values.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CoinViewHolder {
val view: View = LayoutInflater.from(p0.context).inflate(R.layout.news_item_view, p0, false)
return CoinViewHolder(view)
}
class CoinViewHolder(itemView: View): ViewHolder(itemView) {
var newsTitle: TextView = itemView.findViewById(R.id.item_title)
}
}
And my main activity:
package com.hfad.cointask
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.widget.Toast
import com.hfad.cointask.adapter.CoinAdapter
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.News
import com.hfad.cointask.model.NewsList
import com.hfad.cointask.service.CoinClient
import kotlinx.android.synthetic.main.activity_feed.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class FeedActivity : AppCompatActivity() {
var news = mutableListOf<News>()
private lateinit var newsRecyclerView: RecyclerView
private lateinit var newsAdapter: CoinAdapter
private lateinit var layoutManager: LinearLayoutManager
private val client = mCoinClient().build()
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
message.setText(R.string.title_home)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
message.setText(R.string.title_dashboard)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
message.setText(R.string.title_notifications)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feed)
newsRecyclerView = findViewById(R.id.news_recycler_view)
layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
newsRecyclerView.layoutManager = layoutManager
newsAdapter = CoinAdapter(news)
newsRecyclerView.adapter = newsAdapter
getNews().enqueue(object: Callback<NewsList>{
override fun onResponse(call: Call<NewsList>, response: Response<NewsList>) {
var jsonNews = response.body().list
news.clear()
news.addAll(response.body().list)
newsAdapter.notifyDataSetChanged()
}
override fun onFailure(call: Call<NewsList>?, t: Throwable?) {
val toast = Toast.makeText(this#FeedActivity, t.toString(), Toast.LENGTH_SHORT)
toast.show()
}
})
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
private fun getNews() = client.getFeed()
class mCoinClient {
private val builder = Retrofit
.Builder()
.baseUrl("https://api.cointelegraph.com/")
.addConverterFactory(GsonConverterFactory.create())
private val retrofit: Retrofit by lazy {
builder.build()
}
private val client: CoinClient by lazy {
retrofit.create(CoinClient::class.java)
}
fun build() = client
}
}
Thanks for any help
I think it you should change your Interface to
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call<Your Object that you want to receive it with>
}
This is the new file I've added in addition to the existing Persistenceconfig.Java. I'm getting this entity manager as 2 found.
we're not using any xml config except for the jpa repositories in spring-data.xml
The issues is occuring only for one package created newly for logging activity and that is also included in jpa repository.
Before adding the below class, everything is normal previously
package com.jumbotree.kumcha.config;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
//import org.springframework.orm.hibernate5.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
//#PropertySource("classpath:kumcha.properties")
#PropertySource("file:/opt/jumbotree/kumcha2/kms.properties")
#EnableJpaRepositories(basePackages = "com.jumbotree.kumcha.crm.model", entityManagerFactoryRef = "createEntityManagerFactoryChargingBean", transactionManagerRef = "createChargingTransactionManagerBean")
#ImportResource("classpath:spring-data.xml")
public class ChargingPersistenceConfig {
#Autowired
private Environment env;
private static final Logger LOGGER = LoggerFactory.getLogger(ChargingPersistenceConfig.class);
#Bean
public DataSource createChargingDataSourceBean() {
LOGGER.info("Charging Datasource created...");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("charging.db.driver"));
dataSource.setUrl(env.getProperty("charging.db.url"));
dataSource.setUsername(env.getProperty("charging.db.username"));
dataSource.setPassword(env.getProperty("charging.db.password"));
return dataSource;
}
//Here is the entity manager added and causing this issue
#Bean //(name = "entityManagerFactoryCharging")
#PersistenceContext (unitName="chargingPU")
public FactoryBean<EntityManagerFactory> createChargingEntityManagerFactoryBean(#Qualifier("createChargingDataSourceBean") DataSource dsc) {
LocalContainerEntityManagerFactoryBean chargingentityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
LOGGER.info("Charging entityman created...");
try {
chargingentityManagerFactoryBean.setDataSource(createChargingDataSourceBean());
chargingentityManagerFactoryBean.setPackagesToScan("com.jumbotree.kumcha.crm.model");
chargingentityManagerFactoryBean.setJpaVendorAdapter(createJpaVendorAdapterBean());
chargingentityManagerFactoryBean.setJpaProperties(createJpaProperties());
} catch (Exception e) {
// TODO: handle exception
LOGGER.error(e.toString());
}
return chargingentityManagerFactoryBean;
}
private Properties createJpaProperties() {
LOGGER.info("hibernate.show_sql :::: "+env.getProperty("hibernate.show_sql"));
return new Properties() {
{
// setProperty("hibernate.hbm2ddl.auto", "create-drop");
setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
// setProperty("hibernate.cache.use_second_level_cache", "true");
// setProperty("hibernate.cache.provider_class", "org.hibernate.cache.EhCacheProvider");
// setProperty("shared-cache-mode", "DISABLE_SELECTIVE");
//<property name="hibernate.cache.use_second_level_cache" value="true"/>
//setProperty("hibernate.connection.zeroDateTimeBehavior", "convertToNull");
}
};
}
private JpaVendorAdapter createJpaVendorAdapterBean() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
// jpaVendorAdapter.setDatabase(Database.valueOf(env.getProperty("db.name")));
jpaVendorAdapter.setShowSql(true);
// jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setDatabasePlatform(env.getProperty("hibernate.dialect"));
return jpaVendorAdapter;
}
#Bean //(name = "transactionManager")
public PlatformTransactionManager createChargingTransactionManagerBean() throws Exception {
LOGGER.info("Charging transactionMan created...");
JpaTransactionManager chargingtransactionManager = new JpaTransactionManager();
chargingtransactionManager.setEntityManagerFactory(createChargingEntityManagerFactoryBean(createChargingDataSourceBean()).getObject());
return chargingtransactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor createPersistenceExceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
// Required if using Hibernate 4
#Bean
public PersistenceExceptionTranslator createPersistenceExceptionTranslatorBeaan() {
return new HibernateExceptionTranslator();
}
}
LoggerRepository cannot choose one of the 2 beans createChargingEntityManagerFactoryBean and createEntityManagerFactoryBean
Make one of them primary and/or specify qualifier. (BTW sometimes even with qualifier it's necessary to make one of beans primary)
Cannot suggest cconfig changes without your code.
I want to verify if a method is called at least once through mockito verify. I used verify and it complains like this:
org.mockito.exceptions.verification.TooManyActualInvocations:
Wanted 1 time:
But was 2 times. Undesired invocation:
Using the appropriate VerificationMode:
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
verify(mockObject, atLeast(2)).someMethod("was called at least twice");
verify(mockObject, times(3)).someMethod("was called exactly three times");
For Kotlin:
build gradle:
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
code:
interface MyCallback {
fun someMethod(value: String)
}
class MyTestableManager(private val callback: MyCallback){
fun perform(){
callback.someMethod("first")
callback.someMethod("second")
callback.someMethod("third")
}
}
test:
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.mock
...
val callback: MyCallback = mock()
val uut = MyTestableManager(callback)
uut.perform()
val captor: KArgumentCaptor<String> = com.nhaarman.mockitokotlin2.argumentCaptor<String>()
verify(callback, times(3)).someMethod(captor.capture())
assertTrue(captor.allValues[0] == "first")
assertTrue(captor.allValues[1] == "second")
assertTrue(captor.allValues[2] == "third")
For Java:
Lombok used to simplify. You can also type out the constructor if you prefer.
build gradle:
testImplementation "org.mockito:mockito-core:3.6.28"
code:
// MyCallback.java
public interface MyCallback {
void someMethod(String value);
}
// MyTestableManager.java
public class MyTestableManager {
private MyCallback callback;
public MyTestableManager(MyCallback callback) {
this.callback = callback;
}
public void perform(){
callback.someMethod("first");
callback.someMethod("second");
callback.someMethod("third");
}
}
test:
import org.mockito.Mockito.times;
import org.mockito.Mockito.verify;
import org.mockito.Mock;
import org.mockito.Captor;
// whatever other imports you need
#Mock
private MyCallback callback;
#Captor
private ArgumentCaptor<String> captor;
private MyTestableManager uut = new MyTestableManager(callback);
// in your test method:
uut.perform()
verify(callback, times(3)).someMethod(captor.capture())
assertTrue(captor.getAllValues().get(0) == "first")
assertTrue(captor.getAllValues().get(1) == "second")
assertTrue(captor.getAllValues().get(2) == "third")
If I get the following json from a RESTful client, how do I elegantly unmarshal the java.util.Date? (Is it possible without providing (aka. hard-coding) the format, that's what I mean by elegantly...)
{
"class": "url",
"link": "http://www.empa.ch",
"rating": 5,
"lastcrawl" : "2009-06-04 16:53:26.706 CEST",
"checksum" : "837261836712xxxkfjhds",
}
The cleanest way is probably to register a custom DataBinder for possible date formats.
import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomDateBinder extends PropertyEditorSupport {
private final List<String> formats;
public CustomDateBinder(List formats) {
List<String> formatList = new ArrayList<String>(formats.size());
for (Object format : formats) {
formatList.add(format.toString()); // Force String values (eg. for GStrings)
}
this.formats = Collections.unmodifiableList(formatList);
}
#Override
public void setAsText(String s) throws IllegalArgumentException {
if (s != null)
for (String format : formats) {
// Need to create the SimpleDateFormat every time, since it's not thead-safe
SimpleDateFormat df = new SimpleDateFormat(format);
try {
setValue(df.parse(s));
return;
} catch (ParseException e) {
// Ignore
}
}
}
}
You'd also need to implement a PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import grails.util.GrailsConfig;
import java.util.Date;
import java.util.List;
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry reg) {
reg.registerCustomEditor(Date.class, new CustomDateBinder(GrailsConfig.get("grails.date.formats", List.class)));
}
}
and create a Spring-bean definition in your grails-app/conf/spring/resources.groovy:
beans = {
"customEditorRegistrar"(CustomEditorRegistrar)
}
and finally define the date formats in your grails-app/conf/Config.groovy:
grails.date.formats = ["yyyy-MM-dd HH:mm:ss.SSS ZZZZ", "dd.MM.yyyy HH:mm:ss"]
Be aware that the new version of Grails 2.3+ supports this type of feature out of the box.
See Date Formats for Data Binding
With that said, if you are forced to use a version of Grails prior to 2.3, the CustomEditorRegistrar
can be updated using the following code to eliminate the deprecation warning, and also uses the #Component annotation, which allows you to remove / skip the step of adding the bean directly in resources.groovy.
Also not that I changed the grails configuration property name to grails.databinding.dateFormats, which matches the property now supported in Grails 2.3+. Finally, my version is a .groovy, not .java file.
import javax.annotation.Resource
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry
import org.springframework.stereotype.Component
#Component
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
#Resource
GrailsApplication grailsApplication
public void registerCustomEditors(PropertyEditorRegistry reg){
def dateFormats = grailsApplication.config.grails.databinding.dateFormats as List
reg.registerCustomEditor(Date.class, new CustomDateBinder(dateFormats))
}
}