szczepaniakdominik
szczepaniakdominik

Reputation: 472

How to remove repetetive unit tests?

Let's say I have the following class:

class Foo {
    public a( a, b ) {
        return this._execute( {a,b} );
    }
    
    public b( a ) {
        return this._execute( a );
    }

    private _execute( a ) {
        if ( ...condition... ) {
            throw new Error("Validation failed!"); 
        }

        return a;
    }
}

And I'd like to prepare a set of unit tests for it. Should I add a test that should check the condition both for a() and b()? Maybe only one method should check it? If so, which one? Or maybe I should use some other approach?

My goal is to reduce the number of unit tests and in this case, I've got the same test for both methods.

Upvotes: 0

Views: 45

Answers (1)

VLAZ
VLAZ

Reputation: 29090

You can split your code into components that are dealing with one thing only:

class Foo {
    private _validator;
    
    constructor(validator) {
      this._validator = validator;
    }
    
    public a( a, b ) {
        return this._validator.validate( {a,b} );
    }
    
    public b( a ) {
        return this._validator.validate( a );
    }
}

class MyValidator {
  public validate( value ) {
      if ( /*...condition...*/ ) {
          throw new Error("Validation failed!"); 
      }

      return value;
  }
}

Now you can test MyValidator as much as you want to make sure it handles inputs as you want. The tests will be applicable for both Foo#a and Foo#b.

What you want to do next is to verify that the methods do use the validator, so in a test setup you can create a mock validator and instantiate const foo = new Foo( mockValidator ). Then the test can look like this:

test( "a() uses the validator", () => {
    //arrange
    const mockValidator = { validate: jest.fn() };
    const foo = new Foo( mockValidator );
    const value = "something";
    
    //act
    foo.a( value );
    
    //assert
    expect(mockValidator.validate).toHaveBeenCalledWith(value);
}

test( "a() does not swallow the validation error", () => {
    //arrange
    const mockValidator = { validate: () => { throw Error("mock") } };
    const foo = new Foo( mockValidator );
    
    //act + assert
    expect(() => foo.a( "" )).toThrow();
}

Note: I'm using Jest for illustrative purposes. The same can be achieved using other testing libraries.

Upvotes: 1

Related Questions