Hi I forgot to say that I have already committed the solution proposed, if someone wants to play a little bit with it.
regards, Leonardo 2014-01-31 Leonardo Uribe <[email protected]>: > Hi > > Trying to improve this stuff I finally found a way to make a junit > runner. Look at this example that summarizes the possible syntax: > > @RunWith(MyFacesTestRunner.class) > @TestConfig( > scanAnnotations = true, > oamAnnotationScanPackages = "my.custom.testpackage", > webappResourcePath = "webapp", > expressionFactory = "com.sun.el.ExpressionFactoryImpl") > @TestServletListeners({ > "org.apache.webbeans.servlet.WebBeansConfigurationListener" > }) > @DeclareFacesConfig("/test-faces-config.xml") > public class MyFacesRunnerSimpleTestCase > { > @TestContainer > private MyFacesContainer container; > > @Inject > @Named("helloWorld") > private HelloWorldController helloWorldBean; > > @BeforeRequest > public void doFilterStart() > { > System.out.println("Filter start..."); > } > > @AfterRequest > public void doFilterEnd() > { > System.out.println("Filter end..."); > } > > @Test > public void testHelloWorld() throws Exception > { > container.startViewRequest("/helloWorld.xhtml"); > container.processLifecycleExecute(); > Assert.assertEquals("page2.xhtml", helloWorldBean.send()); > container.renderResponse(); > > container.getClient().inputText("mainForm:name", "John Smith"); > // The button end current request and start a new request > // with a simulated submit > container.getClient().submit("mainForm:submit"); > > container.processLifecycleExecute(); > Assert.assertEquals("John Smith", helloWorldBean.getName()); > Assert.assertEquals("/page2.xhtml", > container.getFacesContext().getViewRoot().getViewId()); > container.endRequest(); > } > > } > > Instead make an specific JSF-CDI runner, I used the SPI stuff we > already have in place to make a JSF JUnit runner that use > InjectionProvider spi to do the injection stuff over the test > instance. I also introduced a new annotation called @TestConfig to > declare the test configuration and another annotation > @TestServletListeners to register a Servlet Listener into the runner. > For example, to make it work with OWB, you only need to register the > listener and that's it. If you want to try Weld, nothing more simple, > just register its listener and that's it. I think it will also work > with Spring. > > The advantage of use an annotation for configuration and another > annotation for register servlet listeners is that you can modify the > configuration just extending the class and overriding the annotation > with a new configuration. I'm still thinking about how to to load a > servlet listener and in that way the configuration by default using > something similar to SPI but using a resource in the classpath. For > example, a simple text file in > META-INF/services/org.apache.myfaces.mc.test.core.runner.ServletListeners > with the servlet listener class names to load for MyFacesTestRunner. > > This is still work in progress, but any suggestion about how to > improve the syntax is most welcome. > > regards, > > Leonardo Uribe > > 2014-01-25 Leonardo Uribe <[email protected]>: >> Hello Kito >> >> It is not possible to include it into MyFaces-Test by several reasons: >> >> - MyFaces Core has a dependency over MyFaces Test. This code is in >> between both projects, so if we include it into MyFaces Test it means >> we will have a circular dependency between both projects. >> - The code includes some small but important changes for MyFaces Core. >> It is easier to maintain these details if the resulting module is >> released along with MyFaces Core, and it will be easier for users to >> know which version of the module is compatible with, because the >> released module will have the same release version numbers as MyFaces >> Core. >> - This code is needed to test Myfaces Core code, because we have >> already a bunch of test cases using this stuff. >> >> The code has been used for some time, and the idea is improve it and >> give the opportunity to users to include it into their projects, and >> also use it in other MyFaces projects in the future. >> >> regards, >> >> Leonardo >> >> >> 2014-01-25 Kito Mann <[email protected]>: >>> Hello Leonardo, >>> >>> This sounds very cool. Why not add it to MyFaces-Test instead? >>> >>> >>> On Saturday, January 25, 2014, Leonardo Uribe <[email protected]> wrote: >>>> >>>> Hi >>>> >>>> With JSF 2.0/2.1 and with the introduction of JSF 2.2, it has become more >>>> and >>>> more frequent to find cases where you have a JSF-CDI application, and you >>>> want >>>> to create JUnit tests. Usually, the interest in these cases is test some >>>> complex server side logic, but the problem is you usually need some >>>> control of >>>> the JSF lifecycle, or there is an interaction between pages and beans and >>>> with a mocked environment like the one provided in MyFaces Test you can >>>> only >>>> simulate partially the beans. In these cases, it is not necessary to fully >>>> simulate the client, because what you really want to check is what's going >>>> on >>>> in the server side. >>>> >>>> Solutions like the one provided by JSFUnit or Arquillian does not fit >>>> properly >>>> in these cases, because the server does not run on the same side as the >>>> client, >>>> so there are 2 running classloaders (the one where junit is and the other >>>> that belongs to the web server) which makes debugging difficult. >>>> >>>> Additionally, some time ago, these issues were opened in MYFACESTEST issue >>>> tracker: >>>> >>>> - MYFACESTEST-42 Implement support for creating managed beans from >>>> faces-config.xml or other JSF config files. >>>> - MYFACESTEST-59 Move MockViewDeclarationLanguageFactory from MyFaces Core >>>> to >>>> MyFaces-test >>>> - MYFACESTEST-62 Move FaceletTestCase from internal MyFaces to the MyFaces >>>> Test project >>>> >>>> These issues suggest it would be great to have some mock test environment >>>> that >>>> can do things like build a view from a .xhml or read faces-config.xml or >>>> .taglib.xml files. In few words, run MyFaces Core in a JUnit test. >>>> >>>> Looking for a way to fix this problem, some time ago it was created this >>>> issue: >>>> >>>> https://issues.apache.org/jira/browse/MYFACES-3376 Create abstract test >>>> classes >>>> that runs MyFaces Core as in a >>>> container >>>> >>>> Inside MyFaces Core Impl module, in src/test/java directory there is a >>>> package >>>> called org.apache.myfaces.mc.test.core with these junit base test classes: >>>> >>>> - AbstractMyFacesTestCase >>>> - AbstractMyFacesRequestTestCase >>>> - AbstractMyFacesFaceletsTestCase >>>> - AbstractMyFacesCDIRequestTestCase >>>> >>>> These classes creates a mock servlet test environment that is able to run >>>> MyFaces Core using JUnit. For example: >>>> >>>> // 1. In pom.xml it is necessary to make available src/main/webapp >>>> resources >>>> // as test resources: >>>> >>>> <build> >>>> <testResources> >>>> <testResource> >>>> <directory>${project.basedir}/src/test/resources</directory> >>>> </testResource> >>>> <testResource> >>>> <directory>${project.basedir}/src/main/webapp</directory> >>>> <targetPath>webapp</targetPath> >>>> </testResource> >>>> </testResources> >>>> <!-- .... --> >>>> >>>> // 2. Add beans.xml in src/main/java/META-INF and src/test/java/META-INF >>>> // 3. Now create the test class >>>> >>>> public class SimpleTestCase extends AbstractMyFacesCDIRequestTestCase >>>> { >>>> @Inject >>>> @Named("helloWorld") >>>> private HelloWorldController helloWorldBean; >>>> >>>> @Test >>>> public void testHelloWorld() throws Exception >>>> { >>>> startViewRequest("/helloWorld.xhtml"); >>>> processLifecycleExecute(); >>>> Assert.assertEquals("page2.xhtml", helloWorldBean.send()); >>>> renderResponse(); >>>> >>>> client.inputText("mainForm:name", "John Smith"); >>>> // The button end current request and start a new request >>>> // with a simulated submit >>>> client.submit("mainForm:submit"); >>>> >>>> processLifecycleExecute(); >>>> Assert.assertEquals("John Smith", helloWorldBean.getName()); >>>> Assert.assertEquals("/page2.xhtml", >>>> facesContext.getViewRoot().getViewId()); >>>> endRequest(); >>>> } >>>> >>>> @Override >>>> protected ExpressionFactory createExpressionFactory() >>>> { >>>> // By default it uses a mock ELFactory. >>>> return new com.sun.el.ExpressionFactoryImpl(); >>>> } >>>> >>>> @Override >>>> protected String getWebappContextFilePath() >>>> { >>>> // By default it is the package name of the test. >>>> return "webapp"; >>>> } >>>> >>>> } >>>> >>>> The example simulates a helloworld submit. getWebappContextFilePath() >>>> defines >>>> the link between the webapp context as test resource, to allow the test to >>>> load the resources from that location. All faces-config.xml and >>>> .taglib.xml >>>> from the classpath are automatically loaded. JSF annotation scanning is >>>> disabled by default but you can enable it overriding isScanAnnotations() >>>> method and setting up "org.apache.myfaces.annotation.SCAN_PACKAGES" param >>>> to reduce the time spent in classpath scanning. >>>> >>>> This code has allowed us to make very complex tests inside MyFaces Core >>>> very >>>> easily, like Faces Flows, Resource Library Contracts, Reset Values or View >>>> Pooling. The resulting simulated environment is almost identical in >>>> comparison >>>> with the one created inside a web server, and the differences can be fixed >>>> quite easily, overriding the appropiate methods. The integration with >>>> CDI is just register the servlet listener and that's it. >>>> >>>> If users are using some JSF third-party component library, it is quite >>>> easy >>>> to create a custom mock client and use Firebug or something else to check >>>> the http requests and provide some methods to fill the simulated client >>>> side logic. >>>> >>>> Create test cases is pretty straightforward, because everything is running >>>> in the junit test case, so you don't need to write any callback, just >>>> write >>>> the instructions and do the necessary validations in the right spots. This >>>> is >>>> how a redirect is simulated: >>>> >>>> @Test >>>> public void testRedirect1() throws Exception >>>> { >>>> startViewRequest("/redirect1.xhtml"); >>>> processLifecycleExecute(); >>>> renderResponse(); >>>> client.submit("mainForm:submit"); >>>> processLifecycleExecuteAndRender(); >>>> // redirect sends 302 response, so the client must take it and >>>> // start the redirected request >>>> client.processRedirect(); >>>> // this is the lifecycle of the redirected request. >>>> processLifecycleExecuteAndRender(); >>>> String redirectedContent = getRenderedContent(); >>>> Assert.assertTrue(redirectedContent.contains("Redirected Page")); >>>> } >>>> >>>> The code tries to reuse as much configuration info as possible. For >>>> example >>>> a test case like FlowMyFacesRequestTestCase in my machine in Netbeans >>>> takes 2.5 seconds to be executed and then 0.17 seconds per each additional >>>> test, and the IDE takes another 3 or 4 seconds to execute >>>> "compile on save" and start junit. CDI takes about 1.1 seconds per method. >>>> Note this is a lot less than deploy a server like jetty or tomee, which in >>>> the same conditions could take (with a helloworld app) about 10 seconds or >>>> more to start, run the test and stop. >>>> >>>> The proposal I have for the consideration of MyFaces community is create a >>>> new module for MyFaces Core 2.2 branch called impl-test. The module takes >>>> the code from org.apache.myfaces.mc.test.core in impl module and repackage >>>> it into a new jar file so users can reference it into its own projects. >>>> In that way we can use the code in MyFaces Core like we are doing right >>>> now and we can maintain it at the same time. >>>> >>>> This is the issue in jira to keep track of this feature: >>>> >>>> https://issues.apache.org/jira/browse/MYFACES-3849 >>>> >>>> I have already committed the necessary code so you can just take it from >>>> trunk and try it. If no objections, I'll include the module into the next >>>> release of MyFaces Core. This is also a good moment to provide ideas about >>>> how to improve this feature, so suggestions are welcomed. >>>> >>>> regards, >>>> >>>> Leonardo Uribe >>> >>> >>> >>> -- >>> ___ >>> >>> Kito D. Mann | @kito99 | Author, JSF in Action >>> Virtua, Inc. | http://www.virtua.com | JSF/Java EE training and consulting >>> http://www.JSFCentral.com | @jsfcentral >>> +1 203-998-0403 >>> >>> * Listen to the Enterprise Java Newscast: http://www.enterprisejavanews.com >>> * JSFCentral Interviews Podcast: >>> http://www.jsfcentral.com/resources/jsfcentralpodcasts/ >>> * Sign up for the JSFCentral Newsletter: http://oi.vresp.com/?fid=ac048d0e17 >>>
