2

I have this simple test, it isn't even a test as I'm simply trying to mock the messagesource.

I'm getting this error:

org.springframework.context.NoSuchMessageException: No message 
found under code '' for locale 'null'.

Can anyone else verify this behavior?

This is a minimum spring boot skeleton test I set up because ReloadableResourceBundleMessageSource didn't work in another project, so I thought I'll just try it in the smallest unit possible.

@RunWith(MockitoJUnitRunner.class)
public class DemoApplicationTests {

    @Test
    public void contextLoads() {

    ReloadableResourceBundleMessageSource messageSource = 
    mock(ReloadableResourceBundleMessageSource.class);
    when(
       messageSource.getMessage(
           anyString(), 
           any(Object[].class), 
           any(Locale.class)
         )
       ).thenReturn("returnValue");

    System.out.println("test");

    } 
}

This is the error I get:

org.springframework.context.NoSuchMessageException: No message 
found under code '' for locale 'null'.

at org.springframework.context.support.AbstractMessageSource.getMessage(
AbstractMessageSource.java:159)
at com.example.DemoApplicationTests.contextLoads(
DemoApplicationTests.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at ....
....
....
....
com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(
JUnit4IdeaTestRunner.java:117)
at com.intellij.rt.execution.junit.JUnitStarter.
prepareStreamsAndStart(
JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(
JUnitStarter.java:74)

It seems to me that it's not able to mock it properly. I've also tried to mock the MessageSource interface to no avail.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
OddBeck
  • 835
  • 8
  • 19
  • 6
    I don't know what your question is, but the reason why it's not possible to stub `getMessage` is because it's declared `final` in `AbstractMessageSource`, which is the superclass of `ReloadableResourceBundleMessageSource`. Mockito can't stub final methods. – Dawood ibn Kareem Dec 02 '15 at 07:57
  • Thanks, that explains it! – OddBeck Dec 02 '15 at 11:00

6 Answers6

4

As David Wallace said:

Mockito can't stub "final" methods, and as 'getMessage' is declared final you can't mock it.

OddBeck
  • 835
  • 8
  • 19
  • Your answer is basically a rip-off of @DavidWallace comment. You could have extended your answer by mentioning PowerMock which is able to [mock final methods](https://code.google.com/p/powermock/wiki/MockFinal) – Roman Vottner Dec 02 '15 at 11:19
  • 3
    The fact that it's a copy of my comment doesn't make it wrong. OddBeck did the right thing by posting this, whereas I was too lazy. He/she even attributed it properly. This does not deserve a downvote. – Dawood ibn Kareem Dec 02 '15 at 17:36
  • Thanks David W! @RomanVottner: I wasn't aware of PowerMock, and if I would have been aware of the issue with Mockito & final methods I would probably not have asked this question at all. – OddBeck Dec 03 '15 at 09:48
3

I didn't want to use PowerMock, and I solved the problem creating a real ReloadableResourceBundleMessageSource bean and injected it into the class I wanted to test

// create the real bean
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setDefaultEncoding("UTF-8");
messageSource.setCacheSeconds(-1);
messageSource.setBasenames("classpath:conf-test/messages"); // this folder is just for testing and it contains a messages_en.properties file

// inject the bean into the class I wanted to test
ReflectionTestUtils.setField(service, "messageSource", messageSource);
Abel ANEIROS
  • 6,029
  • 2
  • 25
  • 19
1

I found a work-around the mockito limitations to mocking final methods in this case.

Instead of mocking, I set the class after constructing it like this:

ReloadableResourceBundleMessageSource bundleMessageSource = new ReloadableResourceBundleMessageSource();
bundleMessageSource.setUseCodeAsDefaultMessage(true);

According to the official documentation:

Return whether to use the message code as default message instead of throwing a NoSuchMessageException. Useful for development and debugging. Default is "false".

Sebastian D'Agostino
  • 1,575
  • 2
  • 27
  • 44
0

Final methods can't mock. So I write a solution like this.

    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setUseCodeAsDefaultMessage(true);
    ReflectionTestUtils.setField(service, "messageSource", messageSource);
ElitCenk
  • 302
  • 3
  • 10
0

I have successfully mocked ResourceBundleMessageSource combining Mockito and PowerMockito (according to this thread).

Here's an extract of my Unit Test :

@RunWith(PowerMockRunner.class)
@PrepareForTest({..., ResourceBundleMessageSource.class})
public class ClassUnderTest {
   @Mock
   ResourceBundleMessageSource resourceBundleMessageSourceMock;


   @Test
   public void myTest() {
      // Given
      ...;

      // When
      PowerMockito.mockStatic(ResourceBundleMessageSource.class);
      
      Mockito.when(resourceBundleMessageSourceMock.getMessage(ArgumentMatchers.anyString(), ArgumentMatchers.any(Object[].class),
        ArgumentMatchers.any(Locale.class)).thenReturn("aString");

      // Then
      ...;
   }

} 
yallouch
  • 21
  • 5
0

Might be a ClassLoader problem, the Tests run differently.

In my case I had multiple maven modules, app and bl. So the Test run in bl and the message.properties was in app/src/main/resource.

Test your actual Classpath of the Test with:

[...]
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
System.out.println(new File(getClass().getClassLoader().getResource(".").getFile()));
[...]

This gave me something like "C:/ws/myapp/bl/target/test-classes"

Solution in the Test was to set the ClassLoader of the actual test class and copy the resource file to the test resource folder of the bl module.

  messageSource.setBundleClassLoader(getClass().getClassLoader());
  messageSource.setBasename("messages");
R.A
  • 1,813
  • 21
  • 29