If you have an interest in Browser Test Automation, you have probably heard about the rising popularity of Cypress. I have enthusiastically joined the bandwagon after many years of being underwhelmed by Selenium.
That being said, Cypress is a young tool compared to Selenium and there remain some open questions about some of the fine points of applying Cypress to APEX (if you’re new to APEX, learn more about it here). In this post, I will be specifically exploring how to bypass the APEX login for unit testing.
Why should we bypass the APEX login for unit testing?
According to the Cypress best practices, using my login UI before each test is an anti-pattern: “Do not use your UI to login before each test.” To summarize the reasoning in my own words, the chief advantages of bypassing the login UI are:
- Speed up your unit tests, where every second counts
- Avoid adding extraneous sources of failure to your unit tests – i.e. don’t test your login UI unecessarily
Applying this best-practice with Oracle APEX may not be self-evident because APEX requires a lot more than simply your username and password to authenticate your session (this complexity plausibly makes the authentication process more secure). I kicked off my exploration into this topic with a post to Stack Overflow, which, as of the time of this writing, did not get a definitive response. After further research into the subject, I’ve come away with some suggestions : 3 methods for bypassing the APEX login for unit testing
Quick aside: Some particulars on the mechanics of APEX authentication
Before we launch into the 3 methods, let’s take a moment to discuss how the APEX browser authentication process works. Beyond your username and password, APEX further requires a series of time-sensitive tokens to authenticate you. The full list of these tokens may change from one APEX version to the next and may also be responsive to the eccentricities of your authentication scheme.
You can confirm what parameters your authentication scheme requires by consulting the HTTP POST request made to the wwv_flow.accept address (using tamper data or recording your login using Apache Jmeter. I’ve made a video about recording your login through Jmeter here. )
From my observations, a successful authentication requires that I provide APEX with:
- A valid session id (or instance id)
- A page submission id
- My username and password (of course)
- And a page items protected id
After a successful login, all further navigation through your APEX application depends on just 2 things:
- A valid session
- A valid cookie
Method 1: login through the GUI (but only once)
This is the most intuitive method and it doesn’t require that you change any of your APEX / database configuration. Then again, it relies on using your login GUI which may be slow, at least by the standards of unit testing.
In the following cypress script, I take the following steps:
- I login to my APEX login GUI in a ‘before’ hook.
- In this same hook, I extract the resulting url (with authenticated session id) and cookie.
- In all subsequent unit tests, I programmatically set the cookie and manipulate the authenticated url to match my intended destination.
(note: don’t be confused by the ‘data-cy’ references – I make a practice of adding these attributes to all the page elements I interact with using Cypress, as per another best practice.)
Edit : Per the comments below this blog post – using Cypress.Cookies.preserveOnce(‘ORA_WWV_APP_100’) is a more elegant approach that allows you to dispense with set the app_cookie variable and then setting the cookie in each unit test. However, if you are switching pages in every unit test, you may find that the less elegant approach (the above) is more reliable.
Method 2: Getting session and cookie with PL/SQL
This is the most elegant and speediest solution of the 3 but requires that someone with SYS access to grant restricted access to a very sensitive APEX view.
2.1 Create a view to access session id and cookie
Your test user only needs a valid session id and cookie to authenticate a session. This information is available in a highly sensitive table called wwv_flow_sessions$ in your APEX schema. To avoid any risks associated with selecting from this view, I propose that you create a view that only displays this data for the username you use to test your application and grant it to a non-privileged schema.
2.2 Rest enable your schema
Your goal to make this data available to your Cypress code so you have rest enable your non-privileged schema.
2.3 Prepare a module, template and get handler
As your now rest-enabled user, create a module, template and get handler to display the requisite session id and cookie value.
Prepare your GET handler to run a PL/SQL block:
Your GET handler should read out your test user’s cookie and session id value after creating a session if one doesn’t already exist:
2.4 Test your web service
Test your RESTful service in your browser by adapting the following link: http://localhost/32181/ords/cypress_user/mysession/test_user/
2.5 Use your RESTful service in your Cypress code
You can now retrieve the output of your RESTful service by using cy.request. In the following cypress script, I take the following steps:
- I fetch an authenticated session id and cookie value in a ‘beforeEach’ hook
- In all subsequent unit tests, I merely assemble my intended url using the authenticated session id
N.B. : Make sure you don’t deploy this configuration to Production.
Method 3: Use a ‘No Authentication’ ‘Switch in Session’ scheme
A 3rd solution is to apply an authentication scheme in your Development environment that doesn’t require authentication. The constraint is you still want an authentication scheme in your Production environment. I propose enabling a ‘Switch in Session’ ‘No Authentication’ scheme to which you apply a build to prevent deployment to Production. If the unit test you are looking to perform has authentication as a dependency, you can further add, say, a Post-Authentication Procedure Name to programmatically login.
3.1 Create a ‘No Authentication’ authentication scheme in your Development environment
Don’t opt for the ‘make current’ option for this 2nd authentication schema. You are not replacing your existing authentication scheme.
Enable ‘Switch in session’ for this authentication scheme.
3.2 Prevent this 2nd authentication scheme from being used in Production
You don’t want this authentication scheme accidentally deployed to production. The following code will check whether a build option named ‘DEV_ONLY is set to ‘include’. If it is not set to ‘include’, it will not enable this authentication scheme.
This code pairs with the following build, which you’ll need to add to your Development environment:
Putting this all together, in the following cypress script, I take the following steps:
- I visit the login page with the APEX_AUTHENTICATION parameter in the url in a ‘beforeEach’ hook
- In all subsequent unit tests, I can visit any page in the application I need so long as I continue to include the APEX_AUTHENTICATION parameter in the url.
Thanks for reading. The above list of suggested approaches to unit testing APEX is by no means exhaustive and hopefully more and better approaches emerge over time. Can you think of other ways to bypass the APEX login? Please let me know if you have any questions about how to implement any of this.