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
>>>

Reply via email to