Hi Kito

KM>> Hmm.. okay. But doesn't this sort of make MyFacesTest obsolete?
Or am I missing something...

This is based on myfaces-test, so you will still use it. There are
situations where the only thing you need is MyFacesTest, specially
when the lifecycle is not relevant and you only need to test some
specific method in a bean or something like that. I see it more like
an evolution, like add more tools to the box.

KM>> I'm really digging the syntax, Leonardo. Will this still work
with regular JSF managed beans?

Of course, but you cannot inject managed beans into the test, because
JSF managed beans does not handle proxies. To get a bean just use
something like:

        container.getApplication().evaluateExpressionGet(
            container.getFacesContext(), "#{myBean}", MyBean.class);

KM>> Also, is this dependent on the latest versions of Core, or can it
work with older versions?

Since this is something new, and it uses the new api for
InjectionProvider, it is dependent on the latest version of Core
(2.2.x). The idea is release this stuff along with the same version of
myfaces core, to avoid incompatibility problems.

regards,

Leonardo

2014-01-31 Kito Mann <[email protected]>:
> I'm really digging the syntax, Leonardo. Will this still work with regular
> JSF managed beans?
>
> Also, is this dependent on the latest versions of Core, or can it work with
> older versions?
>
> ___
>
> 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
>
>
> On Fri, Jan 31, 2014 at 2:19 PM, Leonardo Uribe <[email protected]> wrote:
>>
>> 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