src/test/java/ru/indvdum/jpa/tests/AbstractJPAEntityTest.groovy
author indvdum (gotoindvdum[at]gmail[dot]com)
Sun, 23 Dec 2012 03:24:53 +0400
changeset 20 a05948e9458c
parent 15 7d8a7e7635d2
permissions -rw-r--r--
Interface type of fields processing
indvdum@2
     1
package ru.indvdum.jpa.tests
indvdum@2
     2
;
indvdum@2
     3
indvdum@2
     4
import static org.junit.Assert.*
indvdum@2
     5
indvdum@2
     6
import java.lang.reflect.Field
indvdum@2
     7
import java.lang.reflect.Method
indvdum@2
     8
import java.lang.reflect.Modifier
indvdum@2
     9
indvdum@2
    10
import javax.persistence.EmbeddedId
indvdum@2
    11
import javax.persistence.Entity
indvdum@2
    12
import javax.persistence.GeneratedValue
indvdum@2
    13
import javax.persistence.Id
indvdum@2
    14
import javax.persistence.IdClass
indvdum@2
    15
import javax.persistence.Transient
indvdum@2
    16
indvdum@2
    17
import org.junit.Test
indvdum@2
    18
indvdum@2
    19
import ru.indvdum.jpa.dao.JPADataAccessObject
indvdum@20
    20
import ru.indvdum.jpa.entities.AbstractEntity
indvdum@2
    21
indvdum@2
    22
indvdum@2
    23
/**
indvdum@2
    24
 * JUnit test case for testing of a creating, listing, updating and removing 
indvdum@2
    25
 * operations with database of the JPA entities.
indvdum@2
    26
 * 
indvdum@2
    27
 * @author 	indvdum (gotoindvdum@gmail.com)
indvdum@2
    28
 * @since 23.12.2011 22:54:26
indvdum@2
    29
 *
indvdum@2
    30
 */
indvdum@2
    31
abstract class AbstractJPAEntityTest extends AbstractJPATest {
indvdum@2
    32
indvdum@2
    33
	protected def uniqueValue = 1
indvdum@2
    34
	protected JPADataAccessObject dao = null
indvdum@2
    35
	protected Set toRemove = new HashSet()
indvdum@10
    36
	protected Map<Class, Integer> existedEntitiesCount = [:]
indvdum@2
    37
indvdum@2
    38
	@Test
indvdum@2
    39
	public void testEntity() {
indvdum@2
    40
		dao = createDAO()
indvdum@2
    41
		try {
indvdum@2
    42
			testEntity(getEntityClass())
indvdum@10
    43
		} catch (Throwable t) {
indvdum@10
    44
			dao.rollback()
indvdum@10
    45
			throw t
indvdum@2
    46
		} finally {
indvdum@2
    47
			dao.close()		
indvdum@2
    48
		}
indvdum@2
    49
	}
indvdum@2
    50
	
indvdum@2
    51
	/**
indvdum@2
    52
	 * @return Your implementation of JPADataAccessObject
indvdum@2
    53
	 */
indvdum@2
    54
	protected abstract JPADataAccessObject createDAO();
indvdum@2
    55
indvdum@2
    56
	/**
indvdum@2
    57
	 * Test creating, listing, updating, and removing of an {@code entityClass} object
indvdum@2
    58
	 * 
indvdum@2
    59
	 * @param entityClass
indvdum@2
    60
	 */
indvdum@2
    61
	protected void testEntity(Class entityClass) {
indvdum@2
    62
		
indvdum@2
    63
		assert AbstractEntity.class.isAssignableFrom(entityClass)
indvdum@2
    64
indvdum@2
    65
		Map<String, Object> fieldsValues
indvdum@2
    66
		def dbEntity
indvdum@2
    67
indvdum@2
    68
		// creating
indvdum@2
    69
		def entity = createEntity(entityClass)
indvdum@2
    70
		testCreatedEntity(entity)
indvdum@2
    71
indvdum@2
    72
		// listing
indvdum@2
    73
		toRemove.each {
indvdum@2
    74
			assert dao.list(it.class).each { it2 ->
indvdum@2
    75
				assert it.class.isAssignableFrom(it2.class)
indvdum@2
    76
			}.size() == toRemove.findAll { it2 ->
indvdum@2
    77
				it.class.isAssignableFrom(it2.class)
indvdum@10
    78
			}.size() + existedEntitiesCount[it.class]
indvdum@2
    79
		}
indvdum@2
    80
indvdum@2
    81
		// updating
indvdum@2
    82
		fieldsValues = updateFields(entity)
indvdum@2
    83
		assert dao.persist(entity)
indvdum@2
    84
		assert dao.contains(entity)
indvdum@2
    85
		dbEntity = dao.find(entityClass, (entity as AbstractEntity).getIdentifierValue())
indvdum@2
    86
		assert dbEntity != null
indvdum@2
    87
		assert checkEntityFieldValues(dbEntity, fieldsValues)
indvdum@2
    88
		testUpdatedEntity(entity)
indvdum@2
    89
indvdum@2
    90
		// removing
indvdum@2
    91
		assert dao.remove(toRemove)
indvdum@2
    92
		assert !dao.contains(toRemove)
indvdum@2
    93
		testRemovedEntity(entity)
indvdum@2
    94
	}
indvdum@2
    95
	
indvdum@2
    96
	/**
indvdum@2
    97
	 * Create and persist to database an {@code entityClass} object
indvdum@2
    98
	 * 
indvdum@2
    99
	 * @param entityClass
indvdum@2
   100
	 * @return created entity object
indvdum@2
   101
	 */
indvdum@2
   102
	protected Object createEntity(Class entityClass) {
indvdum@20
   103
		if (entityClass.isInterface())
indvdum@20
   104
			return null;
indvdum@2
   105
		assertNotNull entityClass.annotations.find {it instanceof Entity}
indvdum@2
   106
		def entity = entityClass.newInstance()
indvdum@2
   107
		assert entity.class == entityClass
indvdum@2
   108
		toRemove.add(entity)
indvdum@10
   109
		if (!existedEntitiesCount.containsKey(entityClass))
indvdum@10
   110
			existedEntitiesCount[entityClass] = dao.list(entityClass).size()
indvdum@2
   111
		
indvdum@2
   112
		Map<String, Object> fieldsValues = updateFields(entity)
indvdum@2
   113
		assert dao.persist(entity)
indvdum@2
   114
		assert dao.contains(entity)
indvdum@2
   115
		def dbEntity = dao.find(entityClass, (entity as AbstractEntity).getIdentifierValue())
indvdum@2
   116
		assert dbEntity != null
indvdum@2
   117
		assert checkEntityFieldValues(dbEntity, fieldsValues)
indvdum@2
   118
		
indvdum@2
   119
		return entity
indvdum@2
   120
	}
indvdum@2
   121
indvdum@2
   122
	/**
indvdum@2
   123
	 * Update all fields of an {@code entity} object
indvdum@2
   124
	 * @param entity
indvdum@2
   125
	 * @return generated field values
indvdum@2
   126
	 */
indvdum@2
   127
	protected Map<String, Object> updateFields(Object entity) {
indvdum@2
   128
		Map<String, Object> fieldsValues = new HashMap<String, Object>()
indvdum@2
   129
		getFields(entity).grep {
indvdum@2
   130
			it.annotations.find {
indvdum@2
   131
				it instanceof Transient || it instanceof GeneratedValue
indvdum@2
   132
			} == null
indvdum@2
   133
		}.each {
indvdum@2
   134
			def newValue = generateFieldValue(entity, it)
indvdum@2
   135
			setFieldValueBySetter(entity, it, newValue)
indvdum@2
   136
			fieldsValues.put(it.name, newValue)
indvdum@2
   137
		}
indvdum@2
   138
		return fieldsValues
indvdum@2
   139
	}
indvdum@2
   140
	
indvdum@2
   141
	/**
indvdum@2
   142
	 * Collections and arrays will not be processed
indvdum@2
   143
	 * 
indvdum@2
   144
	 * @param entity
indvdum@2
   145
	 * @param field
indvdum@2
   146
	 * @return generated field value
indvdum@2
   147
	 */
indvdum@2
   148
	protected Object generateFieldValue(Object entity, Field field) {
indvdum@2
   149
		def type = field.getType()
indvdum@2
   150
		def newValue
indvdum@2
   151
		if(type.toString() == 'boolean' || type == Boolean.class) {
indvdum@2
   152
			newValue = (boolean) (uniqueValue++ % 2i == 0i)
indvdum@2
   153
		} else if(type.toString() == 'byte' || type == Byte) {
indvdum@2
   154
			newValue = (byte) (uniqueValue++ % Byte.MAX_VALUE + 1i)
indvdum@2
   155
		} else if(type.toString() == 'char' || type == Character) {
indvdum@2
   156
			newValue = (char) (uniqueValue++ % (int) Character.MAX_VALUE + 1i)
indvdum@2
   157
		} else if(type.toString() == 'short' || type == Short) {
indvdum@2
   158
			newValue = (short) (uniqueValue++ % Short.MAX_VALUE + 1i)
indvdum@2
   159
		} else if(type.toString() == 'int' || type == Integer) {
indvdum@2
   160
			newValue = (int) (uniqueValue++ % Integer.MAX_VALUE + 1i)
indvdum@2
   161
		} else if(type.toString() == 'long' || type == Long) {
indvdum@2
   162
			newValue = (long) uniqueValue++ % Long.MAX_VALUE + 1L
indvdum@2
   163
		} else if(type.toString() == 'float' || type == Float) {
indvdum@2
   164
			newValue = (float) uniqueValue++ % Float.MAX_VALUE + 1f
indvdum@2
   165
		} else if(type.toString() == 'double' || type == Double) {
indvdum@2
   166
			newValue = (double) uniqueValue++ % Double.MAX_VALUE + 1d
indvdum@15
   167
		} else if(Number.class.isAssignableFrom(type)) {
indvdum@15
   168
			// trying to use constructor with int argument
indvdum@15
   169
			newValue = type.newInstance(uniqueValue++ % Byte.MAX_VALUE + 1i)
indvdum@2
   170
		} else if(type == String.class) {
indvdum@2
   171
			newValue = (String) "test${uniqueValue++}"
indvdum@20
   172
		} else if(type instanceof Class && ((type as Class).isInterface() || (type as Class).annotations.find {it instanceof Entity} != null)) { // modifying of a primary keys is deprecated
indvdum@2
   173
			// an attempt to use already created entities
indvdum@2
   174
			def currentValue = getFieldValue(entity, field)
indvdum@20
   175
			newValue = toRemove.find {it.class.isAssignableFrom(type) && it != currentValue}
indvdum@2
   176
			if(newValue == null)
indvdum@2
   177
				newValue = createEntity(type as Class)
indvdum@14
   178
		} else if(Enum.class.isAssignableFrom(type)) {
indvdum@14
   179
			def values = type.values();
indvdum@14
   180
			newValue = values[uniqueValue++ % values.size()];
indvdum@2
   181
		} else if(
indvdum@2
   182
				type instanceof Class 
indvdum@2
   183
				&& !(
indvdum@2
   184
					field.clazz.annotations.find {it instanceof IdClass} != null 
indvdum@2
   185
					&& field.declaredAnnotations.find {it instanceof Id} != null
indvdum@2
   186
				)
indvdum@2
   187
				&& field.declaredAnnotations.find {it instanceof EmbeddedId} == null
indvdum@2
   188
				&& !Collection.class.isAssignableFrom(type)
indvdum@2
   189
				&& !(type as Class).isArray()
indvdum@2
   190
			) { // modifying of a primary keys is deprecated
indvdum@2
   191
			newValue = (type as Class).newInstance()
indvdum@2
   192
		} else {
indvdum@2
   193
			newValue = getFieldValue(entity, field)
indvdum@2
   194
		}
indvdum@2
   195
		return newValue
indvdum@2
   196
	}
indvdum@2
   197
indvdum@2
   198
	/**
indvdum@2
   199
	 * @param entity
indvdum@2
   200
	 * @param rightValues
indvdum@2
   201
	 * @return {@code true}, if all entity fields is equals to {@code rightValues}
indvdum@2
   202
	 */
indvdum@2
   203
	protected boolean checkEntityFieldValues(Object entity, Map<String, Object> rightValues) {
indvdum@2
   204
		def result = true
indvdum@2
   205
		def fields = getFields(entity)
indvdum@2
   206
		rightValues.each {  key, value ->
indvdum@2
   207
			fields.find { it.name == key }.each {
indvdum@2
   208
				if(!getFieldValue(entity, it).equals(value)) {
indvdum@2
   209
					result = false
indvdum@2
   210
				}
indvdum@2
   211
			}
indvdum@2
   212
		}
indvdum@2
   213
		return result
indvdum@2
   214
	}
indvdum@2
   215
indvdum@2
   216
	/**
indvdum@2
   217
	 * Set a {@code field} to a {@code value} of an entity {@code object}
indvdum@2
   218
	 * 
indvdum@2
   219
	 * @param object
indvdum@2
   220
	 * @param field
indvdum@2
   221
	 * @param value
indvdum@2
   222
	 */
indvdum@2
   223
	protected void setFieldValue(Object object, Field field, Object value) {
indvdum@2
   224
		boolean isAccessible = field.accessible
indvdum@2
   225
		field.accessible = true
indvdum@2
   226
		field.set(object, value)
indvdum@2
   227
		field.accessible = isAccessible
indvdum@2
   228
	}
indvdum@2
   229
	
indvdum@2
   230
	/**
indvdum@2
   231
	 * Trying set a {@code field} to a {@code value} of an entity {@code object}
indvdum@2
   232
	 * by using a setter method. If setter not found, field will be set directly
indvdum@2
   233
	 * throw {@code setFieldValue} method.
indvdum@2
   234
	 * 
indvdum@2
   235
	 * @param object
indvdum@2
   236
	 * @param field
indvdum@2
   237
	 * @param value
indvdum@2
   238
	 */
indvdum@2
   239
	protected void setFieldValueBySetter(Object object, Field field, Object value) {
indvdum@2
   240
		boolean isSetted = false
indvdum@2
   241
		getMethods(object).grep {
indvdum@2
   242
			(
indvdum@2
   243
				Modifier.isPublic(it.modifiers) 
indvdum@2
   244
				&& it.parameterTypes.size() == 1 
indvdum@2
   245
				&& (
indvdum@2
   246
					value == null 
indvdum@2
   247
					|| it.parameterTypes[0].isAssignableFrom(value.class)
indvdum@2
   248
					)
indvdum@2
   249
				&& it.name =~ /^set(?i:${field.name.charAt(0)})${field.name.replaceFirst("^.{1}", "")}/
indvdum@2
   250
			)
indvdum@2
   251
		}.each {
indvdum@2
   252
			object."${it.name}"(value)
indvdum@2
   253
			isSetted = true
indvdum@2
   254
		}
indvdum@2
   255
		if(isSetted)
indvdum@2
   256
			return
indvdum@2
   257
		setFieldValue(object, field, value)
indvdum@2
   258
	}
indvdum@2
   259
indvdum@2
   260
	/**
indvdum@2
   261
	 * @param object
indvdum@2
   262
	 * @param field
indvdum@2
   263
	 * @return the field value of an object
indvdum@2
   264
	 */
indvdum@2
   265
	protected Object getFieldValue(Object object, Field field) {
indvdum@2
   266
		boolean isAccessible = field.accessible
indvdum@2
   267
		field.accessible = true
indvdum@2
   268
		def value = field.get(object)
indvdum@2
   269
		field.accessible = isAccessible
indvdum@2
   270
		return value
indvdum@2
   271
	}
indvdum@2
   272
indvdum@2
   273
	/**
indvdum@2
   274
	 * @param obj
indvdum@2
   275
	 * @return all object fields, including inherited
indvdum@2
   276
	 */
indvdum@2
   277
	protected Collection<Field> getFields(obj) {
indvdum@2
   278
		Class clazz = obj.getClass()
indvdum@2
   279
		Collection<Field> fields = clazz.declaredFields
indvdum@2
   280
		while(clazz.superclass != null) {
indvdum@2
   281
			clazz = clazz.superclass
indvdum@2
   282
			fields.addAll(clazz.declaredFields)
indvdum@2
   283
		}
indvdum@2
   284
		fields.grep {
indvdum@2
   285
			!it.synthetic &&
indvdum@2
   286
					!Modifier.isStatic(it.modifiers) &&
indvdum@2
   287
					!Modifier.isTransient(it.modifiers)
indvdum@2
   288
		}
indvdum@2
   289
	}
indvdum@2
   290
indvdum@2
   291
	/**
indvdum@2
   292
	 * @param obj
indvdum@2
   293
	 * @return all object methods, including inherited
indvdum@2
   294
	 */
indvdum@2
   295
	protected Collection<Method> getMethods(obj) {
indvdum@2
   296
		Class clazz = obj.getClass()
indvdum@2
   297
		Collection<Method> methods = clazz.declaredMethods
indvdum@2
   298
		while(clazz.superclass != null) {
indvdum@2
   299
			clazz = clazz.superclass
indvdum@2
   300
			methods.addAll(clazz.declaredMethods)
indvdum@2
   301
		}
indvdum@2
   302
		methods.grep {
indvdum@2
   303
			!it.synthetic &&
indvdum@2
   304
					!Modifier.isStatic(it.modifiers) &&
indvdum@2
   305
					!Modifier.isTransient(it.modifiers)
indvdum@2
   306
		}
indvdum@2
   307
	}
indvdum@2
   308
	
indvdum@2
   309
	/**
indvdum@2
   310
	 * For implement in successors
indvdum@2
   311
	 *
indvdum@2
   312
	 * @param entity
indvdum@2
   313
	 */
indvdum@2
   314
	protected void testCreatedEntity(Object entity) {
indvdum@2
   315
		
indvdum@2
   316
	}
indvdum@2
   317
	
indvdum@2
   318
	/**
indvdum@2
   319
	 * For implement in successors
indvdum@2
   320
	 *
indvdum@2
   321
	 * @param entity
indvdum@2
   322
	 */
indvdum@2
   323
	protected void testUpdatedEntity(Object entity) {
indvdum@2
   324
		
indvdum@2
   325
	}
indvdum@2
   326
	
indvdum@2
   327
	/**
indvdum@2
   328
	 * For implement in successors
indvdum@2
   329
	 *
indvdum@2
   330
	 * @param entity
indvdum@2
   331
	 */
indvdum@2
   332
	protected void testRemovedEntity(Object entity) {
indvdum@2
   333
		
indvdum@2
   334
	}
indvdum@2
   335
indvdum@2
   336
	/**
indvdum@2
   337
	 * @return an {@link AbstractEntity} successor
indvdum@2
   338
	 */
indvdum@2
   339
	abstract protected Class getEntityClass()
indvdum@2
   340
}