Stop using CSS Class Selectors in Jasmine Tests: Here’s a Better Way



This content originally appeared on DEV Community and was authored by Andreas Nicolaou

When writing end-to-end or integration tests, how we select elements matters. A lot.

If you’ve ever updated a class name in a component and had half your test suite crumble like a stale cookie, you’ve experienced first-hand why CSS classes aren’t the most stable way to identify elements in tests.

That’s exactly why I built @andreasnicolaou/eslint-plugin-no-classes-by-css an ESLint plugin designed to enforce better testing practices by preventing the use of CSS class selectors in By.css() calls.

https://www.npmjs.com/package/@andreasnicolaou/eslint-plugin-no-classes-by-css

Let me walk you through the why, the what, and the how.

Why Avoid CSS Classes in Tests?
In Jasmine (and Protractor) tests, a common pattern for locating elements is:

element(By.css('.login-button'));

Looks fine, right? Until your designer decides to rename .login-button to .btn-primary, and now your test is broken-not because the app changed functionally, but because the selector changed superficially.

Using class selectors tightly couples your test code to styling decisions. This is fragile and leads to:

  • Brittle tests that fail on non-functional changes
  • Hard-to-maintain test suites
  • Poor separation of concerns

IDs or tag-based selectors tend to be more semantically meaningful and less likely to change arbitrarily.

The Plugin: @andreasnicolaou/eslint-plugin-no-classes-by-css

This ESLint plugin enforces a simple rule: Don’t use CSS class selectors in By.css().

It flags the following:

// ❌ Not allowed
element(By.css('.my-class'));

But allows these if configured:

// ✅ Allowed (with allowIds)
element(By.css('#submit-button'));
// ✅ Allowed (with allowTags)
element(By.css('button'));

Getting Started
1. Install the plugin
Depending on how ESLint is set up in your project:

# If ESLint is installed globally
npm install -g @andreasnicolaou/eslint-plugin-no-classes-by-css
# Or locally in your devDependencies
npm install -D @andreasnicolaou/eslint-plugin-no-classes-by-css
@andreasnicolaou/eslint-plugin-no-classes-by-css

@andreasnicolaou/eslint-plugin-no-classes-by-css – npm

ESLint plugin to disallow usage of By.css with CSS classes in Jasmine tests. Latest version: 1.5.1, last published: 17 days ago. Start using @andreasnicolaou/eslint-plugin-no-classes-by-css in your project by running `npm i @andreasnicolaou/eslint-plugin-no-classes-by-css`. There are no other projects in the npm registry using @andreasnicolaou/eslint-plugin-no-classes-by-css.

favicon npmjs.com

2. Configure .eslintrc

{
  "plugins": [
    "@andreasnicolaou/no-classes-by-css"
  ],
  "rules": {
    "@andreasnicolaou/no-classes-by-css/no-classes-by-css": [
      "error",
      {
        "allowIds": true,
        "allowTags": true
      }
    ]
  }
}

Configuration Options

allowIds: (default: false)
 Allow ID selectors like By.css('#user-id')
allowTags: (default: false)
 Allow tag selectors like By.css('input')
disallowClasses: Always true
 Disallows selectors like By.css('.username-field')

Real-World Example
Bad 👎

const loginButton = element(By.css('.login-btn'));

Better 👍

const loginButton = element(By.css('#login'));

Even Better
Use a dedicated test ID or data attribute with a custom selector strategy.

Autofix?
Not yet. The plugin currently does not support autofix, since rewriting selectors correctly often requires human context.

But it will warn you, and that’s a great first step toward cleaner tests.

Final Thoughts

Tests should be resilient and easy to maintain. By avoiding class-based selectors, you’re building test suites that are more stable, more semantic, and less prone to breaking from visual or stylistic changes.
If this sounds like a headache you’ve dealt with before, give @andreasnicolaou/eslint-plugin-no-classes-by-css a spin. Drop it into your config, commit to better practices, and spend less time debugging fragile selectors.
Let’s write tests that can stand the test of time.

Thanks for reading! If you found this helpful, share it with your team - or better yet, drop a star on the GitHub repo. ✨


This content originally appeared on DEV Community and was authored by Andreas Nicolaou