본문 바로가기

Programing/Framework

[스프링] ReloadableResourceBundleMessageSource 관련한 테스트 에러

기존에 spock 테스트 코드가 있었는데, 어느 시점부터 계속 테스트가 깨진다.

해당 코드에서는 테스트 하는 코드는 다국어 지원 메세지가 지원하고 있었기에 주입을 위해 아래와 같이 ReloadableResourceBundleMessageSource를 이용하여 주입해주고 있었다.


class AvailableValidatorTest extends Specification {

def messageSource = new ReloadableResourceBundleMessageSource(

defaultEncoding: CharEncoding.UTF_8,

basenames: [

"classpath:messages/web/validation-error-messages.xml",

"classpath:messages/web/web-validation-error-messages.xml"

]

)

private MyMessageSource orderMessageSource = new MyMessageSource(messageSource, Locale.KOREA)

private MyService checkoutService = Mock(CheckoutService)

private AvailableValidator sut


def setup() {


테스트가 깨지는 곳에서 확인을 해보니...

아래와 같은 DEBUG 로그 메세지가 있었다.

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/web-validation-error-messages.xml] - neither plain properties nor XML

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/web-validation-error-messages.xml_ko] - neither plain properties nor XML

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/web-validation-error-messages.xml_ko_KR] - neither plain properties nor XML

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/validation-error-messages.xml] - neither plain properties nor XML

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/validation-error-messages.xml_ko] - neither plain properties nor XML

DEBUG 2018-02-27 20:02:23 [main] o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties:500 - No properties file found for [classpath:messages/web/validation-error-messages.xml_ko_KR] - neither plain properties nor XML


과거에 해당 로그를 본 적이 있었으면 쉽게 원인을 찾았겠지만, 결국 몰라서 디버그로 MessageSource를 만드는 곳을 따라가서 보았다.


우선 테스트가 안깨지던 커밋으로 체크아웃해서 돌려보고, 다시 원래대로 돌아와서 비교를 해보았다.

메세지 지원은 스프링의 org.springframework.context.MessageSource 인터페이스를 통해서 수행하고 있었다.

이 인터페이스의 구현은 org.springframework.context.support.ReloadableResourceBundleMessageSource 가 하고 있다.

이 ReloadableResourceBundleMessageSource는 AbstractMessageSource를 구현하고 있다.


AbstractMessageSource : resolveCodeWithoutArguments에서 message로 값을 가져와야 하는데 못 가져온다.

protected String getMessageInternal(String code, Object[] args, Locale locale) {
if (code == null) {
return null;
}
if (locale == null) {
locale = Locale.getDefault();
}
Object[] argsToUse = args;

if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
// Optimized resolution: no arguments to apply,
// therefore no MessageFormat needs to be involved.
// Note that the default implementation still uses MessageFormat;
// this can be overridden in specific subclasses.
String message = resolveCodeWithoutArguments(code, locale);
if (message != null) {
return message;
}
}


ReloadableResourceBundleMessageSource : resolveCodeWithoutArguments의 구현은 getMergedProperties에서 값을 가져오고 있었다.

@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
if (this.cacheMillis < 0) {
PropertiesHolder propHolder = getMergedProperties(locale);
String result = propHolder.getProperty(code);
if (result != null) {
return result;
}
}
else {
for (String basename : this.basenames) {
List<String> filenames = calculateAllFilenames(basename, locale);
for (String filename : filenames) {
PropertiesHolder propHolder = getProperties(filename);
String result = propHolder.getProperty(code);
if (result != null) {
return result;
}
}
}
}
return null;

String filename = (String) filenames.get(j);

부분을 지나고 나면 파일명을 디버그해서 볼 수 있는데... 이름이 이상했다.


~~_ko.xml 이런식으로 이름이 붙어야 하는데 ~~.xml_ko 이런식으로 붙고 있었다.


전에 소스코드를 파 보다 보니 확장자는 properties나 xml으로 자동으로 붙여주는 부분이 있었다.

public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
implements ResourceLoaderAware {

private static final String PROPERTIES_SUFFIX = ".properties";

private static final String XML_SUFFIX = ".xml";
protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
long refreshTimestamp = (this.cacheMillis < 0 ? -1 : System.currentTimeMillis());

Resource resource = this.resourceLoader.getResource(filename + PROPERTIES_SUFFIX);
if (!resource.exists()) {
resource = this.resourceLoader.getResource(filename + XML_SUFFIX);
}

.properties 가 있는지 보고 .xml 를 붙인 다음에 찾아보는 식이다.


결국 원인은 붙이지 않아도 될 확장자 때문에 발생하는 것이다.

아래와 같이 .xml 을 제거하니 테스트가 잘 돌았다.

class AvailableValidatorTest extends Specification {

def messageSource = new ReloadableResourceBundleMessageSource(

defaultEncoding: CharEncoding.UTF_8,

basenames: [

"classpath:messages/web/validation-error-messages.xml",

"classpath:messages/web/web-validation-error-messages.xml"

]

)

private MyMessageSource orderMessageSource = new MyMessageSource(messageSource, Locale.KOREA)

private MyService checkoutService = Mock(CheckoutService)

private AvailableValidator sut


def setup() {


덕분에 스프링 소스코드 공부했다.