automative

by

photo

Should Tests Be WET or DRY?

October 23, 2024

In the world of software development, you’ll often come across two contrasting principles when writing tests: WET (Write Everything Twice) and DRY (Don’t Repeat Yourself). These principles guide developers on how to structure their code to enhance readability, maintainability, and efficiency.

The DRY principle emphasizes the importance of avoiding repetition in your codebase, ensuring that each piece of logic is defined only once. You can dive deeper into the DRY principle in this Don’t Repeat Yourself Wikipedia article.

Understanding the difference between WET and DRY testing is crucial because I've often seen how quickly things can become difficult to manage. When tests aren't structured thoughtfully from the start, it can be challenging and time-consuming to refactor them later on.


WET Tests

WET testing refers to a testing style where some code repetition is allowed in the test cases. This is often seen as a trade-off for readability, especially when the tests are simple.

Pros:

  • Simplicity: WET tests can be more straightforward and easy to understand, especially for beginners.
  • Isolation: Each test is independent, meaning you don’t need to worry about side effects between tests.
  • Quick to write: In smaller codebases, or when writing simple tests, you may find WET tests easier and faster to implement.

Example:

describe('Login Tests - WET', () => {
  test('should allow user to login with valid credentials', async () => {
    await page.goto('https://example.com/login');
    await page.fill('input[name="username"]', 'user1');
    await page.fill('input[name="password"]', 'password1');
    await page.click('button[type="submit"]');
    await expect(page).toHaveURL('https://example.com/dashboard');
  });

  test('should show an error with invalid credentials', async () => {
    await page.goto('https://example.com/login');
    await page.fill('input[name="username"]', 'user1');
    await page.fill('input[name="password"]', 'wrongpassword');
    await page.click('button[type="submit"]');
    await expect(page).toHaveText('Invalid username or password');
  });
});

In this WET test, the code for filling out the login form and clicking the submit button is repeated in both test cases.


DRY Tests

DRY is a principle where you aim to avoid repeating code. In the context of tests, this means abstracting shared logic into reusable functions or setup steps, reducing duplication.

Pros:

  • Less redundancy: DRY tests are more efficient, especially for larger projects where the same logic might be needed across multiple tests.
  • Easier maintenance: When test logic changes, you only need to update it in one place.
  • Cleaner tests: DRY tests tend to be more concise and focused on testing the behavior rather than setting up the same steps over and over.

Example:

describe('Login Tests - DRY', () => {
  const login = async (username, password) => {
    await page.goto('https://example.com/login');
    await page.fill('input[name="username"]', username);
    await page.fill('input[name="password"]', password);
    await page.click('button[type="submit"]');
  };

  test('should allow user to login with valid credentials', async () => {
    await login('user1', 'password1');
    await expect(page).toHaveURL('https://example.com/dashboard');
  });

  test('should show an error with invalid credentials', async () => {
    await login('user1', 'wrongpassword');
    await expect(page).toHaveText('Invalid username or password');
  });
});

In this DRY test, the common login functionality has been abstracted into the login function, avoiding repetition.

This example may be simple, but the goal of this blog is to provide a clear understanding of the pros and cons of WET and DRY approaches, helping you decide which is more suitable for your testing needs.


When to Use WET vs. DRY

From my personal experience, I highly recommend having a clear understanding of the requirements before you start writing tests. On a broader level, if your goal is simply to create a few test cases, avoid creating variables for selectors that you only use once or twice. However, if you're building a larger suite of tests with similar, critical application flows, the DRY principle can be invaluable. Using DRY will make your test suite easier to maintain in the long run.

Use WET when:

  • Your tests are simple and self-contained.
  • You're dealing with code that's unlikely to change frequently.
  • You need clarity and simplicity in your tests.

Use DRY when:

  • You have repeated logic in multiple tests.
  • You're working in a larger codebase where duplication could lead to maintainability issues.
  • You need to refactor your tests for better clarity and efficiency.

Conclusion

While the DRY principle is generally recommended, it's important to balance readability and maintainability. In some cases, writing slightly repetitive tests (WET) can enhance clarity, especially when the tests are short and focused. However, for larger and more complex test suites, applying DRY leads to cleaner, more maintainable code.

Ultimately, the goal should be test clarity and maintainability, so choose the approach that best suits your project's scale and complexity