Launch HN: Reflect (YC S20) – No-code test automation for web apps
We worked together for 5+ years at a tech start-up that deployed multiple times a week. After every deployment, a bunch of our developers would manually smoke test the application by running through all of the critical user experiences. This manual testing was expensive in terms of our time. To speed up the tests’ run time, we dedicated developer resources to writing and managing Selenium scripts. That was expensive at “compile time” due to authoring and maintenance. At a high-level, we believe that the problem with automated end-to-end testing comes down to two things: tests are too hard to create, and they take too much time to maintain. These are the two issues we are trying to solve with Reflect.
Reflect lets you create end-to-end tests just by using your web application, and then executes that test definition whenever you want: on a schedule, via API trigger or simply on-demand. It emails you whenever the test fails and provides a video and the browser logs of the execution.
One knee-jerk reaction we’re well aware of: record-and-playback testing tools, where the user creates a test automatically by interacting with their web application, have traditionally not worked very well. We’re taking a new approach by loading the site-under-test inside of a VM in the cloud rather than rely on a locally installed browser extension. This eliminates a class of recording errors due to existing cookies, proxies or other extensions introducing state that the test executor is not aware of, and unifies the test creation environment with the test execution environment. By completely controlling the test environment we can also expose a better UX for certain actions. For example, to do visual testing you just click-and-drag over the element(s) you want to test. For recording file uploads we intercept the upload request in the VM, prompt you to upload a file from your local file system, and then store that file in the cloud and inject it into the running test. If you want to add additional steps to an existing test, we’ll put you back into the recording experience and fast-forward you to that point in the test, where again all you need to do is use your site and we’ll add those actions to your existing test. Controlling the environment also allows us to reduce the problem space by blocking actions which you typically wouldn’t want to test, but which are hard to replicate and thus could lead to failed recordings (e.g. changing browser dimensions mid-recording). As an added bonus, our approach requires no installation whatsoever!
We capture nearly every browser action from hovers to file uploads, and drag-and-drops to iframes, while building a repeatable, machine-executable test definition. We support variables for dynamic inputs, and test composition so your test suite is DRY. The API provides flexible integration with your CI/CD out of the box and supports creating tests in prod and running them in staging on the fly. You don’t need to use a separate test grid, as all Reflect tests run on our own infrastructure. Parallel execution of your tests is a two click config change and we don’t charge you extra for it.
Some technical details that folks might find interesting:
- For every action you take we’ll generate multiple selectors targeting the element you interacted with. We wrote a custom algorithm that generates a diverse set of selectors (so that if you delete a class in the future your tests won’t break), and ranks them by specificity (i.e. [data-test-id] > img[alt=”foo”] > #bar > .baz).
- To detect certain actions we have to track DOM mutations across async boundaries. So for example we can detect if a hover ended up mutating an element you clicked on and thus should be captured as a test step, even if the hover occurred within a requestAnimationFrame, XHR...
109 comments
[ 3.8 ms ] story [ 181 ms ] threadI really enjoy the idea of business/marketing people collaborating with tests. Congrats.
I also really like your business model so will give it a try.
- We generate multiple selectors for each element you interact with. So if in the future you change a class or id, we'll fallback to other selectors when running your test. We also only choose a selector if (1) it matches a single element in the DOM, and (2) that element is interactable (e.g. not completely hidden), and (3) if it has text associated with it then we'll only choose it if the text captured at recording time matches the text at run time. This helps prevent us from selecting the wrong element when running a test.
- For React apps, we use heuristics to ignore classes generated by CSS-in-JS classes like Styled Components and EmotionJS. We also have the ability to target elements based on React component name (it requires a one-time setup on your side to enable this)
- For Visual Testing failures (e.g. you've screenshotted an element and now that element has a different UI) we have a simple workflow to 'Accept Changes' and mark the new UI as the new baseline for tests going forward.
Certainly more to do here but this is one of the key problems we're looking to tackle.
1) I can't delete steps
2) I can't hover.
Great product guys, keep it up!
1) You can delete steps on a recorded test but not during the recording. This is to ensure that we have an exact copy of the initial recording. Thereafter, you can click on the test step and click "Delete" on the middle pane.
2) These should be captured out of the box. Can you email me your test URL so I can investigate? fitz at Thank you!
Github OAuth for example will issue a 2FA email-based challenge non-deterministically. We handle that by detecting the challenge and filling out the challenge code based on the contents of the email sent by Github. This requires a one-time setup where you add an email address we control to your Github user so that we can read and parse it.
For Google OAuth we can execute all the steps but the two issues there are (1) we run everything in a single window and some web apps don't like that because they assume the oauth flow will happen in a new window, and (2) sometimes Google prompts you to answer a security question and we don't yet support marking test steps as optional.
What our customers have been doing instead is setting up a mechanism to auto-log in the test user using a magic link. Basically sending in a one-time-use auth code to a URL in their app that then authenticate the user. I think some platforms (Firebase?) have built-in support for this.
I'm certainly happy to brainstorm what could work best for you though if you'd like (my email: todd at reflect.run)
In terms of added security, some options there would be to only enable these "magic links" in staging and only enable this type of auth for a least-privledged user account (e.g. no admin or employee accounts could auth this way).
More just seeking bounds of possibilities, thanks for your replies.
Curious how your service would handle a app that mandates non-email based MFA like SMS or TOTP.
Additionally, what about testing a onboarding flow that might require some form of manual approval?
Thanks!
No support for TOTP unfortunately, but if the SMS or email-based challenge support would work for you feel free to reach out and I can talk to the specifics of the one-time setup - todd at reflect dot run
I warmly recommend _not_ testing Google OAuth and instead passing a token and bypassing it on your server's side.
The way automation can work around it (in Testim.io for example) is "mock network" which is supported in our codeless solution but also available in most test infrastructure (like Playwright which is FOSS). You would mock out the parts that leave the user with a token they can use to authenticate.
It might make sense to allow/recognize these to save trying to find things that way, rather than via CSS-type selectors that may change.
"off the shelf" machine learning makes it pretty easy to create very robust selectors. I gave a talk about it in GDG Israel and was supposed to speak about it in HalfStack that got delayed cancelled because of COVID19 - but the principle is pretty simple.
It's amazing how much RoI you can get from relatively simple models of reinforcement learning. Here are some ideas: https://docs.google.com/presentation/d/1OViIwDJJw1kjVJH5Z2N5...
Good luck :]
Our app's core workflow involves signing in, uploading images and annotation files, parsing those files, using HTML5 canvas to render previews and thumbnails, uploading files to GCS, and kicking off several async functions and ajax requests.
That Reflect.run was able to handle all of those complex operations makes me pretty confident it can effectively test any web app.
If that's the case, curious how you might build a test that can handle something like a MFA token request on login.
The pricing (6 hours of testing for $99) means that if I want to do a 1 minute test with 50 concurrent players, I can only test 6 times a month. A big benefit of testing is to ensure we ship reliable software, and we plan to ship much more often than 6 times a month.
Is there a way you could price by unique tests instead of hours tested?
- Cypress has a really nice local development experience that feels at home to front-end devs. I would describe Reflect as a nice remote recording experience that simulates an end-user. So we're kind of attacking the same problem from a different perspective. You can technically record Reflect tests against your local env with ngrok, but Cypress is certainly a better local testing experience. So advantage Cypress here.
- There are actions like interacting with iframes, drag and drop, links that open a new window, and file uploads that range from difficult to almost impossible to do in Cypress. We support these actions out-of-the-box.
- Similarly if you want to do visual testing in Cypress you'll need to integrate with a third-party visual testing tool like Percy or Applitools. We have visual testing support built-in.
- I've seen folks struggle bridging the gap between using Cypress for local testing and actually getting it set up in their build/deployment pipeline. Since we're a fully managed service it's just a single API call to get Reflect in your CI/CD. We also have built-in scheduling so if you just wanted to run your tests at a fixed schedule you don't need to integrate with anything, which I think is a nice way to ge t going quickly and prove out our tool.
- Because of Cypress's design, it's not so easy to get Cypress tests to run in parallel. This is really important because E2E tests take way longer to run vs. unit and integration tests, and the best lever IMO for reducing this is parallelization. This is also another tripping point for folks getting Cypress set up in CI/CD.
- The final differentiator I'll mention is really the difference between a code-based vs. codeless approach. We're trying to reduce the time it takes to create and maintain tests and we think the way to do that is to basically handle as much as we can automatically on your behalf. Instead of writing code that simulates actions, you just record your actions and we generate the automation for you. So for a flow like adding to cart or user registration, you might only take you a few minutes to set that up in Reflect but it'd be a lot longer to do in Cypress. Certainly as your test suite grows things like reusability become really important, and we support that as well. This also means that non-developers like QA testers w/o dev experience, PMs, designers, and customer support can write and maintain tests.
I am not sure why you think Cypress is hard to parallelize but if you don't like their managed service (dashboard) you can use https://github.com/agoldis/sorry-cypress - it's quite possible to do.
(All the rest of what you wrote sounds solid - good luck again :])
Even if you use something like Cypress you probably need something like Reflect (or Testim where I work but don't represent - for that matter) - you would just end up writing the framework in-house.
But once I started to write tests with it, I realized the model was optimized for simple tests where you can afford to act only as the end-user, whereas for the more complex flows we're interested in testing, you need to act both as the end-user as well as, say, someone in the operations team doing a bunch of manual approval steps, or say, the email/sms system that delivering some secret out-of-band (through a staging backdoor API), interleaved between the end-user actions, and not being able to precisely coordinate those async interactions between these distinct actors made those tests extremely awkward to write compared to something using playwright/puppeteer, where you just coordinate promises like you usually do.
That's before we get into the limitations around single domain per test which meant we couldn't effectively test our login flow that uses a separate domain for login for OIDC.
The "less easy out" is an on-prem version with a contract regarding updates + a clause for what happens if the company goes under in terms of support + an escrow over the code (the company gets a copy of the code + the license to change but not sell it etc).
1) Flakiness / non-determinism 2) The constant change of the UI
Both of these are absolute killers to productivity on a large team. Note, I'm not against UI testing in theory. I think if you could, you would have full end to end tests for every single edge case so that you could be sure that the actual product presented to users work. But in practice, end to end testing an asynchronous distributed system (which a simple client-server application still is) is full of non-determinism.
Re the constant changing of the UI. This is just also true in my experience. I've worked on a navigation / global UI redesign at every company I've ever worked at. It happens like once every 3-5 years. Within the redesigns, it's still extremely common to subtly change the UI for UX reasons all the time. When this happens, be prepared to spend half of the time updating all of your tests.
I think the hardest aspects of testing are fast authoring and stability (maintenance) - AI tools can help with that and learning data between test runs can create very stable tests.
So as someone in this space - it's a very exciting space and I am very optimistic for this startup (Reflect)
We wired up all of the asynchronous bits to tweak a hidden DOM element at the bottom of the page, so that our tests could wait for that element to settle before validating anything.
We'd already had a lot of our async code run through a couple of helper methods (to facilitate cache busting and Karma tests), so it was 'just' a matter of finishing what we'd started.
I kinda feel like the browsers are letting us down by not exposing this sort of stuff.
For instance, your negative tests (click something and make sure nothing happens) can fail to detect anything at all because they perform an action and then validate the UI before the action has even processed.
Yes, of course nothing has happened, because you didn't let it.
Non-determinism is just one issue. The other issue is how frequently the UI changes. For example, a large form can get broken out into a wizard. The UI is completely semantically different, but performs the same functionality.
How do you make such UI changes without your test suite slowing you down?
So all of the tests look something like this (here's a UI form):
So you see the view model is kind of like a Page Object, in that it has an API which represents the user's interaction. But, in this case the view model is what's used in the actual production code, it's not just a testing helper. So you just invoke the API as the user would when interacting with the feature, and verify that the correct data gets sent to the server when the form is submitted. After all, the client's job is primarily to interact with the server and maintain state. The feature is functional if the correct data is sent to the server.Then, let's say you're in React, hooking up this view model is quite easy:
The buttons / inputs just get wired up to the appropriate view model functions and properties, and you can have the component's state just be the view model's state. The view is then just entirely about appearance and forwarding events.I'll then write some tests that actually render the UI, but they're just sanity checks. How often do you get a bug where you failed to call the correct function in response to a button click? The logic and wiring in response to all of the events is the more interesting part.
Edited to improve the code formatting a bit.
I've written my fair share of extensive selenium stuff (and appium, but.. let's forget about those painful memories) and one thing that I found fairly "easy' to add to the suites I had written was accessibility testing (using Deque's Axe[0] tools). It literally took a few lines of code and a flag at runtime to enable the accessibility report on my Jenkins/TestNG/Selenium combo. However WCAG is constantly changing and it's hard to keep up, and even Deque is not always up to date AFAIK. Do you have plans to have accessibility testing with your tool ? (even a subset of WCAG's rules)
Another thing I've noticed is the jump in pricing from Free to 100 USD/month which goes from 30mins to 6hours. This might be steep for a team attempting to test out the validity of the tool against the competition - perhaps offering a first-month discounted trial or something like that would be appealing.
I also haven't really seen if it is possible to enable concurrency (for example, is testing on three platforms at the same time for 10 minutes on that free tier possible? I would imagine so if one is doing the lifting with CI like Jenkins - but perhaps you have your own solutions). Tangentially related but you say your tests integrate with various CI solutions, does this mean one can extract the test results in a way that allows further processing and integration into other tools? (I'm thinking of the XMLs coming out of TestNG there).
Lastly, I don't know if the time used is counted from the moment you start your VMs or browser starts, or the page is loaded, or the first step is done or something else. Clarifying that might help with a team's estimates (I had internal tests where the struggle was entirely on having the virtual environment/device/browser ready and the test was then a breeze, so the significant metric was the boot-up time).
[0] https://github.com/dequelabs/axe-core
1) We capture all attributes for elements and although we don't yet let you fail a test if the attributes are incorrect, that's where we're headed. So, you could imagine enabling/disabling accessibility validation at the account or per-test level. This is great feedback.
2) Re: pricing - thanks for this idea! Pricing is still evolving, will keep in mind.
3) We support parallel execution at no extra charge; it's only test execution time. So if you want to spin up 10 tests at once, that's fine. (We may need to scale up our infra at the outset, obviously.) There is no hard cap on the number of concurrent tests.
4) Our API (https://reflect.run/docs/developer-api/documentation/) is in the early stages, but includes endpoints for executing tests and fetching results for tests in JSON.
5) Runtime is the time the browser begins navigating to the page. You are not charged for time spent spinning up infrastructure or saving the test run. And just to be clear, you are not charged for any time spent recording tests.
Thanks again!
I think it's all great news, and for customers it's reassuring to know that running time is really just about the test itself (which I noticed is in the FAQ, so sorry for wasting your time there). This makes me think about a couple more things: can clients select zones from which to run the tests? It's probably not a huge focus if your current target customers are in North America, but can become useful if a European company considers the tool and all loading times become noticeable. Or just if a company expects clients from anywhere and wants to test for that (which is basically the hardest bit for a small team, as it starts to involve having some devops experience to spin up VMs on the cloud in different zones and all that jazz). Another tangentially related setting would be the locale for tests execution and scenarios (I'm thinking of date formats, monetary symbols, etc.. with expectations being different if you're in the US or in the UK for example)
The lack of cross-browser testing is something I can see scaring away some teams, especially with mobile being restricted to profiles of older iPhones only. IIRC over 70-80% of iPhone users are on the latest major iOS version. But it all ties in to other issues that I'm sure you're already dealing with (iPhone users using Safari, something like 20% of desktop users using Firefox/Safari, and the odd clients here and there who would have very specific annoying requirement like "I must be able to test on Internet Explorer - yes I know it's deprecated but my startup is B2B and I'm the small fish there").
Your docs might be short but they are already good and readable, especially for a target that might include a mix of pure devs and automation QAs/SDET/etc.
All the best!
However the premise of having a test editor in a VM where I can time travel to a specific point in time and add steps from there could save me a bunch of time. Also the multiple selectors per element and the ability to target React component names sound really cool.
Happy for the competition :)
Excited to see more players in this space - good luck! Most of the market is still doing manual QA and that has to change.