Headshot of Joshua Pendragon

Joshua Pendragon

Blog

Putting things to paper helps me think. Come here to watch the sausage get made.

    Testing Auth in Playwright

    Jun 6, 2024

    ES6 Classes in Playwright ECMAScript

    Over the course of my work with node-based projects, I keep finding myself interally cringing when encountering classes in Javascript/Typescript. Perhaps my gut reaction is because I came into professional programming by being the only person with the time and the willingness to maintain the work. My introduction was heavily JS influenced, and classes were just a buzzword until I was a few years into my career.

    I appreciate and take full advantage of encapsulation and dependency injection, but as I’ve branched into real OO languages (Java, C#), I’ve found myself making assumptions when having to work in JS classes that have come back to bite me. Whenver I see a class in a JS/TS project, I can’t help but think “Why not just use a function?“. Composed, type-protected factory functions and closures feel like they can do everything a class can do, but with less overhead and less cognitive load.


    The Good…

    When the domain is simple and nobody succumbs to the temptation to over-engineer, classes can get the job done and send everyone home happy. Maybe your team got fancy and did a bit of abstract definitions and interfaces, but the code is still readable and maintainable.

    class Animal {
      constructor(name) {
        this.name = name;
      }
      
      speak() {
        console.log(`${this.name} makes a noise.`);
      }
    }
    
    class Dog extends Animal {
      constructor(name) {
        super(name);
      }
      
      speak() {
        console.log(`${this.name} barks.`);
      }
    }
    
    const a = new Animal('Animal');
    a.speak(); // Animal makes a noise.
    
    const d = new Dog('Mitzie');
    d.speak(); // Mitzie barks.
    

    The Bad…

    As best as I’ve been able to tell, the entire reason for explicit class syntax being introduced to the ECMAScript standard was to make it easier for people coming from other OO languages to contribute to front-end projects. Like ENUMS, classes are a syntax feature that was bolted on to the language to make it more palatable to developers from ‘real’ OO languages.

    But in the JS world, prototypes are the real deal. Dressing them up in OO languages cannot change how the language actually works, and the existing implementation of classes can be a bit of a minefield. I will now shamelessly crib from one of my preferred articles on the topic


    JavaScript Pop Quiz: What Does the Following Code Do?

    function Proto() {
      this.name = 'Proto'
      return this;
    }
    
    Proto.prototype.getName = function () {
      return this.name
    }
    
    class MyClass extends Proto {
      constructor() {
        super()
        this.name = 'MyClass'
      }
    }
    
    const instance = new MyClass()
    
    console.log(instance.getName())
    
    Proto.prototype.getName = function () {
      return 'Overridden in Proto'
    }
    
    console.log(instance.getName())
    
    MyClass.prototype.getName = function () {
      return 'Overridden in MyClass'
    }
    
    console.log(instance.getName())
    
    instance.getName = function () {
      return 'Overridden in instance'
    }
    
    
    console.log(instance.getName())
    

    The correct answer is that it prints to console:

    > MyClass
    > Overridden in Proto
    > Overridden in MyClass
    > Overridden in instance
    

    If you answered incorrectly, you don’t understand what class actually is. This isn’t your fault. Much like Array, class is not a language feature, it’s syntactic obscurantism. It tries to hide the prototypical inheritance model and the clumsy idioms that come with it, and it implies that JavaScript is doing something that it is not.


    I had burned myself several times without really knowing why when I first read this article. I find that it’s always cathartic to have someone you’ve never met explain why you’ve been feeling a certain way about a topic. As I’ve done work in both class-y and class-less projects, I’ve found that the class-less projects are easier to maintain and refactor. I’ve also found that the class-y projects encounter more issues as a result of their syntax misleading the developers into thinking they’re working in a language paradigm that they’re not. Everything in JS is an object, and the class syntax is just a way to make it look like the class definitions are immutable.


    In the abstract, using classes is fine. But I feel that if you want to use JS/TS professionally, you are better served by taking the time to understand how the language actually works and, having done so, the appeal of classes will diminish. At the end of the day, the ultimate goal is codifying your intent as clearly as possible. Classes necessarily obfuscate certain aspects of the language, and I find that the more I work with them, the more I find myself fighting the constructs to get them to do what I want.

    Testing Auth in Playwright

    Mar 29, 2024

    Handling Authentication in Automated Tests

    When procrastinating trying to hone my playwright skills, I’ll often go to the help-playwright channel on the Playwright Discord and try to help people with their questions. I find that it’s a good way to learn and to give back to the community. Today, one of the threads was about how to optimize authentication across tests. Variants of this question come up often, so I thought I’d write a bit about it and share my approaches and thoughts.


    OP’s Question

    Is there a way to access a webapp without typing in the username and password?
    I know you can access some webapps by adding the username and password directly into the url, e.g. https://example.com/ but when I try it like that it simply takes me to the login page, where the username and password has to be typed in.
    I also tried to cookies etc. but I didn’t manage to figure out how make that work.
    I am fairly new to this kind of testing.
    How do you guys access a webapp with username and password? (without typing in the credentials)

    My Answer

    It really depends on how your web application handles auth and sessions. In rough order of complexity, optimizing auth across tests can follow this order (IMO):

    GUI > API

    https://playwright.dev/docs/auth#authenticate-with-api-request
    Call the auth API everywhere you would login with the GUI. This is usually the lowest hanging fruit, fairly easy to refactor, and can even be done incrementally. I’ll usually make a small auth module for a project that goes something like:

    /**
     * Authorizer
     * @param page
     * @example
     * const page = new Page();
     * const authorizer = authorizer(page);
     * authorizer.loginWithGui(); // or...
     * authorizer.loginViaApi();
     */
    const authorizer = (page: Page) => {
      const loginWithGui = async () => {
        // login with GUI
      };
      const loginViaApi = async () => {
        // login via API
      };
    
      return {
        loginWithGui, // you will probably keep this to test that your login form works for users
        loginViaApi, // this will ultimately replace the gui login and be used everywhere else.
      };
    };
    

    You might be able to watch your devtools on your browser as you manually log in and identify the API call that does the auth to build the API function logic.

    Cache auth

    https://playwright.dev/docs/auth#session-storage This is the one the docs mostly focus on. This will be a bit faster per test than step 1, since you won’t need to do the API call for each test.

    HOWEVER, if you DO make the auth call with every test, it is very unlikely that your cached session data will become stale, and that IS a potential risk here depending on your SUT timeouts and playwright test run duration.

    Don’t…

    This may seem facetious, and it’s more of a soapbox than an answer, but I’ll add it anyway.

    This difficulty and others like it are good motivators of “shift-left” testing methodology and the “shape” of your test distribution. When looking at the distribution of your tests, you want tens of e2e UI tests, hundreds of integration/service tests, and thousands of unit tests in your SUT.

    Stateful UI tests are expensive and complex. If you find yourself trying to implement auth boilerplate for dozens of tests with user state (or worse, multiple users), there’s a good possibility your pyramid is inverted. If you don’t have it already, try to get buy-in from your other QA engineers and the dev teams to put lower level coverage in place so you don’t HAVE to test EVERYTHING in the GUI.

    While rare, it might also be the case that the playwright coverage you’re trying to add is ALREADY covered by unit and integ tests, and you/your team just didn’t know. Start the conversation with the devs as

    “Hey, end-to-end tests with user auth is slow and expensive.
    Do we already have enough coverage over [Feature x,y,z] in our lower level tests?
    Might it be better to implement that coverage instead, and then only do user-state-less component testing of the GUI of [X,Y Z]? ”

    The First Blog

    Feb 15, 2024

    I was listening to the developers who test blog and the message that “You should try to put out content when you have something to share” resonated strongly.

    I’ll preface by saying that I’ve been in the job market for about 3 months now. The experience has not been fun, per-se, but there have been some good parts. Expanding my network, getting my portfolio and career artifacts (like this website) put together and organized and polished enough to show people have all been beneficial in various ways. I’ve managed to accrue some decent experience in some languages other than Typescript (mostly Go, Rust, and Java), and re-learn how to have healthier work-life balance.

    Much of these benefits came directly or indirectly from leveraging excellent online resources, so I’ll take some time in this post to talk about them, how I used them, and how they might be helpful to others. Nothing is sponsored and YMMV.

    First off: Pathrise.

    Pathrise is a mentorship and placement service, halfway between a bootcamp and a staffing firm on steroids. You can sign up for their service to:

    • Gain access to their mentor network. They have mentors that are industry agnostic for things such as Job Hunting and mindset coaching, as well as industry specific mentors (I was interested in their Software Engineers)
    • Articles, webinars, and other materials.

    I found Pathrise through one of the Facebook eavesdropping adds. Clearly, FB was listening in when my manager broke the news to me and wasted no time in its targeted advertisements.

    Test Automation University is a free resource for learning about test automation. They have courses on Selenium, Cypress, Playwright, and other tools. I’ve taken a few courses on Playwright and Cypress previously, and I’ve found them to be very helpful. I also used them as onboarding material for new test automation team members, particularly the testing fundamentals and playwright intro courses. The courses are taught by industry professionals and are (generally) very well put together.

    I’ve found the courses to be very helpful in learning about the tools and how to use them. You also get certificates for courses and tracks that you complete. It may not have the gravitas of an ISTQB cert, but it’s a good way to show that you’re learning and growing.

    Online Test Websites EG: (the-internet.heroku) (demoqa)

    It’s well and good to learn how to use a tool, but you need to practice using it. These websites are great for that. They give you a variety of different web elements to interact with, and you can use them to practice writing tests. I’ve used them to practice writing tests for things like login forms, tables, and other web elements.