Skip to content

Instantly share code, notes, and snippets.

@tq-jappy
Created January 29, 2017 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tq-jappy/ce452d5c2b951bb67b71185a1e2e2b50 to your computer and use it in GitHub Desktop.
Save tq-jappy/ce452d5c2b951bb67b71185a1e2e2b50 to your computer and use it in GitHub Desktop.
MyBatis mappers automated test with Spock/Spring
package sample.domain.mapper
import org.apache.ibatis.annotations.Delete
import org.apache.ibatis.annotations.Insert
import org.apache.ibatis.annotations.Select
import org.apache.ibatis.annotations.Update
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
import org.springframework.core.type.filter.RegexPatternTypeFilter
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.test.context.ActiveProfiles
import org.springframework.transaction.annotation.Transactional
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.time.LocalDateTime
import java.util.regex.Pattern
@ActiveProfiles("test")
@SpringBootTest(classes = [SomeConfig])
@Transactional
class MapperSpec extends Specification {
static final String mapperPackage = 'sample.domain.mapper'
class Fixture {
Class<?> mapperClass
Method method
}
@Autowired
ApplicationContext context
@Shared
List<Fixture> fixtures = [] as List
def setupSpec() {
fixtures = [] as List
def provider = new ClassPathScanningCandidateComponentProvider(false) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
// interface は除外されてしまうため override
return beanDefinition.getMetadata().isInterface()
}
}
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
def allClasses = provider.findCandidateComponents(mapperPackage).collect {
Class.forName(it.getBeanClassName())
}
allClasses.each { mapperClass ->
for (Method method : mapperClass.methods) {
if (method.getAnnotation(Select)
|| method.getAnnotation(Insert)
|| method.getAnnotation(Update)
|| method.getAnnotation(Delete)) {
def fixture = new Fixture(mapperClass: mapperClass, method: method)
fixtures.add(fixture)
}
// TODO: support @InsertProvider, @UpdateProvider, @DeleteProvider, @SelectProvider
}
}
}
@Unroll
def "#mapperClass.simpleName #method.name"() {
setup:
def mapper = context.getBean(mapperClass)
when:
method.invoke(mapper, createParameters(method.getParameterTypes()))
then:
noExceptionThrown()
where:
mapperClass << fixtures.collect { it.mapperClass }
method << fixtures.collect { it.method }
}
Object[] createParameters(Class<?>[] parameterTypes) {
def parameters = Arrays.asList(parameterTypes).collect { type -> createObject(type) }
println parameters
return parameters.toArray(new Object[parameters.size()])
}
Object createObject(Class<?> type) {
if (type.isPrimitive()) {
if (type == int.class) {
return 0
} else if (type == long.class) {
return 0L
} else if (type == boolean.class) {
return false;
} else {
// TODO:
throw new UnsupportedOperationException("unsupported type: " + type)
}
} else if (type.isEnum()) {
return type.getEnumConstants()[0]
} else {
if (type == Integer.class) {
return Integer.valueOf(0)
} else if (type == Long.class) {
return Long.valueOf(0L)
} else if (type == Boolean.class) {
return Boolean.FALSE;
} else if (type == String.class) {
return "test"
} else if (type == BigDecimal.class) {
return BigDecimal.valueOf(0D)
} else if (type == Pageable.class) {
return new PageRequest(0, 10)
} else if (type == LocalDateTime.class) {
return LocalDateTime.now()
} else if (type == List.class) {
return Collections.emptyList()
} else if (type == Map.class) {
return Collections.emptyMap()
} else {
Object obj = type.newInstance()
for (Field field : type.getDeclaredFields()) {
int modifier = field.getModifiers()
if (!Modifier.isStatic(modifier) && !Modifier.isFinal(modifier)) {
field.setAccessible(true)
field.set(obj, createObject(field.getType()))
}
}
return obj
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment