Announcement Announcement Module
Collapse
No announcement yet.
Injecting messages for validation testing Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Injecting messages for validation testing

    Hopefully there's a quick and easy answer for this one.

    I'm starting unit testing on my project. The class in question is essentially a form with some fields and the following validate method:

    Code:
    public boolean validate(MessageContext context)
    Inside the message, I'm using MessageBuilders to construct the message, using a code to generate the message text referencing the resource bundle for the flow.

    For the purposes of testing, I'm setting various properties of the form, then calling validate() passing in a DefaultMessageContext. However, not surprisingly, the codes I'm passing to my MessageBuilder aren't being evaluated since it isn't referencing my properties file.

    I can't seem to figure out where to configure this so my properties file gets read so my messages resolve correctly. Can someone point me in the right direction?


    As a side question, I'm looking for a way to universally get at my resources. During development, I typically run my test classes directly. But our test classes are also a part of our ant build process. The working directories when executing each are different, so I can't be sure of where I am in the directory structure so it's difficult to figure out how to get to the web/WEB-INF/flows/... etc. This seems like a common problem, so I'm hoping there's a common solution to this one as well.

  • #2
    This should be a common case, right? Load a flow for a test, load a resource bundle, start the test.

    All I need is the recommended way to load a resource bundle.

    Anyone?

    Comment


    • #3
      People ARE unit testing their flows, right? still looking for advice...

      Comment


      • #4
        Have you tried configuring a StaticMessageSource on your DefaultMessageContext?

        Keith

        Comment


        • #5
          That's one good solution, especially when we're just testing a method that needs to be passed a MessageContext object.

          Originally, when I tried to register a message source in the configureFlowBuilderContext() method like so:'

          Code:
          builderContext.registerBean("messageSource", messageSource);
          I got an exception which said a static message source was already registered.
          Last edited by InverseFalcon; Jun 22nd, 2009, 03:56 PM. Reason: My proposed solution does not work

          Comment


          • #6
            Ok, guess I'm not out of the woods yet.

            When I try to resolve a code against the message source, I get this:

            Code:
            org.springframework.context.NoSuchMessageException: No message found under code 'error_char_type_invalid' for locale 'en_US'.
            So, somehow I need to associate the message source (or at least the messages) with the locale.

            So far, all of my attempts to set the message sources or set the parent message sources have failed. I might be able to do something if I can get my hands on the currently registered StaticMessageSource. What's the easiest way to get this so I can modify it?

            Comment


            • #7
              Found one reason why this wasn't working. I was using ReloadableResourceBundleMessageSource's setBasename() method, but was including the full "messages.properties" name at the end instead of just "messages" like you're supposed to.

              Comment


              • #8
                Any Ideas?

                Hi,

                I'm having the same problem. I see no way of binding my messages.properties to my flow test. To summarize:

                When I try to add a message to the MessageContext at a point in my flow, I get:

                Code:
                org.springframework.context.NoSuchMessageException: No message found under code 'my.code' for locale 'de_DE'.
                as InverseFalcon also stated, when I try to register the messageSource:

                Code:
                builderContext.registerBean("messageSource", messageSource);
                I get:

                Code:
                java.lang.IllegalStateException: Could not register object [org.springframework.context.support.ResourceBundleMessageSource: basenames=[messages]] under bean name 'messageSource': there is already object [org.springframework.context.support.StaticMessageSource: {}] bound
                	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.registerSingleton(DefaultSingletonBeanRegistry.java:124)
                	at org.springframework.webflow.test.MockFlowBuilderContext.registerBean(MockFlowBuilderContext.java:72)
                	at my.package.MyFlowExecutionIntTest.configureFlowBuilderContext(MyFlowExecutionIntTest.java:50)
                	at org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests.buildFlow(AbstractExternalizedFlowExecutionTests.java:171)
                	at org.springframework.webflow.test.execution.AbstractExternalizedFlowExecutionTests.getFlowDefinition(AbstractExternalizedFlowExecutionTests.java:147)
                	at org.springframework.webflow.test.execution.AbstractFlowExecutionTests.startFlow(AbstractFlowExecutionTests.java:120)
                	at org.springframework.webflow.test.execution.AbstractFlowExecutionTests.startFlow(AbstractFlowExecutionTests.java:109)
                	at my.package.MyFlowExecutionIntTest.testMyFlow(MyFlowExecutionIntTest.java:68)
                	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                	at java.lang.reflect.Method.invoke(Method.java:597)
                	at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:160)
                	at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:233)
                	at org.springframework.test.context.junit4.SpringMethodRoadie$RunBeforesThenTestThenAfters.run(SpringMethodRoadie.java:333)
                	at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:217)
                	at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:197)
                	at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:143)
                	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:160)
                	at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
                	at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
                	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
                	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
                	at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
                	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
                	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
                	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
                	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
                	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
                	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
                	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
                The comment:

                Have you tried configuring a StaticMessageSource on your DefaultMessageContext?
                is interesting, but I see no entry points to get the DefaultMessageContext, much less add something to it. Does anyone know a way of doing this?

                When I run the app in container, my messages show up. I would like to use the MessageSource I defined in my applicationContext.xml and confirm that the correct validation message is set.

                Any help or suggestions would be appreciated.
                Last edited by wackydog; Jun 30th, 2009, 09:40 AM.

                Comment


                • #9
                  Solutions?

                  Hi there!

                  I'm having the exact same problem - has anyone solved this?

                  -Kim

                  Comment


                  • #10
                    Write Your Own MessageSource

                    So originally we used a StaticMessageSource approach where we created a message source with all the appropriate codes and then tested against it. That became a pain to maintain and so I took an alternate approach:

                    Write your own testMessageSource. Mine looks like this:
                    Code:
                    public class TestMessageSource extends AbstractMessageSource {
                    
                        /**
                         * Resolve code returns a message with the code and ignores Locale
                         * @param code
                         * @param locale
                         * @return
                         */
                        protected MessageFormat resolveCode(String code, Locale locale) {
                    
                            return createMessageFormat(code, locale);
                    
                        }
                    }
                    If you use this as your message source, it will always insert a message with the code itself as teh message. So you can do things like:

                    Code:
                    public void testPhaseIsBlank() throws Exception {
                            TestMessageSource source = new TestMessageSource();
                            DefaultMessageContext ctx = new DefaultMessageContext(source);
                            EditForm epf = new EditForm();
                            epf.setPhase(null);
                             epf.validateSave(ctx);
                            assertMessageExists("editForm.noPhase", ctx);
                    }
                    
                    
                     public static void assertMessageExists(String msg, MessageContext msgs) {
                    
                            for (Message message : msgs.getAllMessages()) {
                                if (message.getText().contains(msg)) {
                                    return;
                                }
                            }
                    
                            Assert.fail("msg '" + msg + "' not found in msgs: " + msgs);
                        }
                    Obviously, this doesn't test that your messages resource contains the right codes but that's a much more complicated issue (because some messagebuilders might specify default messages, etc.). We actually solve that by code analysis (ie, look for all messagebuilders, find all message codes, make sure they are in the resource file) rather than testing.

                    Hope this helps!

                    Comment


                    • #11
                      Found a solution

                      Hi all

                      I found that setting the applicationContext during the configureFlowBuilderContext works for me. All my flow tests are running fine now.

                      Code:
                      public abstract class CommonFlowTest extends AbstractXmlFlowExecutionTests { 
                              @Override 
                              protected void configureFlowBuilderContext(MockFlowBuilderContext serviceRegistry) { 
                                      
                                      try { 
                                              serviceRegistry.getFlowBuilderServices().setApplicationContext(insert your app context.....); 
                                      } catch (BeansException e) { 
                                              e.printStackTrace(); 
                                      } catch (Exception e) { 
                                              e.printStackTrace(); 
                                      }                 
                              } 
                      }
                      -Kim

                      Comment


                      • #12
                        Localized error messages

                        I had this same problem during unit testing--NoSuchMessageException. To get around it, I just specified defaultText("...") in the message building process. If you do not have the bundle key defined the exception gets thrown, but if there is defaultText specified the exception is not thrown.

                        Looking into the Spring Binding sources, the MockRequestContext builds a default message context--there is no way to override this or set it after being built. Perhaps we should open an issue? It could probably be patched just by adding a setter in the Mock. OR have the constructors moved into a factory object and just add another factory method to build a MockRequestContext with the specified message bundle...

                        "People are testing their flows right?" -- of course not! Nobody has time for testing! copy+paste x100 (sarcasm)

                        -David

                        Comment


                        • #13
                          Code:
                          @Override
                          	protected void configureFlowBuilderContext(
                          			MockFlowBuilderContext builderContext) {
                          		Object obj = builderContext.getApplicationContext().getBean(
                          				AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME);
                          		obj = builderContext.getApplicationContext().getBean(
                          				AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME);
                          		if (obj != null && obj instanceof StaticMessageSource) {
                          			StaticMessageSource messageSource = (StaticMessageSource) obj;
                          			messageSource.setUseCodeAsDefaultMessage(true);
                          		}
                          	}
                          Setting setUseCodeAsDefaultMessage to true works for me. Obviously this means that you can't test the true resolution, but at your test won't fail on a NoSuchMessageException.

                          Comment

                          Working...
                          X