Testing Fundamentals

 

Avoiding Implementation Details

Example: SimpleMultiply

 

Requirement:

Create a function that can multiply 2 non zero integers. Negative numbers are out of scope


Assume JS still does not have the * operator

export const sum = (a, b) => a + b;

export const simpleMultiply = (a, b) => {
  let total = 0;
  
  for(let x = 0; x < a; x++) {
    total = sum(total, b);
  }
  
  return total;
}
import {sum, simpleMultiply} from "./simpleMultiply";


jest.mock(add);

describe("multiply", () => {
  // THIS PASSES
  it("should call add 4 times when supplied with 3 & 4", () => {
    multiply(3, 4);
    expect(add).toBeCalledTimes(4);
  });
  
  // THIS PASSES
  it("should call add 5 times when supplied with 3 & 5", () => {
    multiply(3, 5);
    expect(add).toBeCalledTimes(5);
  });
});

Assume * is finally available

export const sum = (a, b) => a + b;

export const simpleMultiply = (a, b) => {
  //   let total = 0;

  //   for(let x = 0; x < a; x++) {
  //     total = sum(total, b);
  //   }

  //   return total;
  return a * b
}
import {sum, simpleMultiply} from "./simpleMultiply";


jest.mock(add);

describe("multiply", () => {
  // THIS FAILS
  it("should call add 4 times when supplied with 3 & 4", () => {
    multiply(3, 4);
    expect(add).toBeCalledTimes(4);
  });
  
  // THIS FAILS
  it("should call add 5 times when supplied with 3 & 5", () => {
    multiply(3, 5);
    expect(add).toBeCalledTimes(5);
  });
});
import {simpleMultiply} from "./simpleMultiply";


jest.mock(add);

describe("multiply", () => {
  // THIS WORKS AGAIN
  it("should return 12 when 3,4 are passed in", () => {
    expect(multiply(3, 4)).toBe(12);
  });
  
  // THIS WORKS AGAIN
  it("should return 15 times when supplied with 3 & 5", () => {
    expect(multiply(3, 5)).toBe(15);
  });
});

Was there an advantage to the first test?

Test Validity Matrix

Having no test is better than having a false negative or false positive test

Why Can A Test Fail

 

  • Regression: Bug in source code that should be fixed
     
  • Change Request: Tests are irrelevant to new functionality. Decide whether to update/delete them.
     
  • Refactoring: Tests have been incorrectly written to begin with. This implies you never actually had a valid test 😩😭🤕

Let's Write A Test For the Following Reducer

export const initialState = {
  todos: [];
  completed: [];
}

const todoReducer = (state = initialState, action) => {
  switch(action.type) {
      case "TODO_ADDED": {
        const todo = action.payload;
        return {
          ...state,
          todos: [...state.todos, todo ],
        }
      }
      
      default:
        return state;
  }
}
import reducer, { initialState } from "./reducer";

describe(() => {
  it("returns the correct initial state", () => {
    expect(reducer(undefined, {})).toEqual(initialState);
  });
});
export const initialState = {
  todos: "pooooooooooooooop";
  completed: [];
}

const todoReducer = (state = initialState, action) => {
  switch(action.type) {
      case "TODO_ADDED": {
        const todo = action.payload;
        return {
          ...state,
          todos: [...state.todos, todo ],
        }
      }
      
      default:
        return state;
  }
}
const todoReducer = (state = initialState, action) => {
  switch(action.type) {
      case "TODO_ADDED": {
        const todo = action.payload;
        return {
          ...state,
          todos: [...state.todos, todo ],
        }
      }
      
      default:
        return {
          todos: [];
          completed: [];
        };
  }
}
import reducer, { initialState } from "./reducer";

describe(() => {
  it("returns the correct initial state", () => {
    expect(reducer(undefined, {})).toEqual({
      todos: [];
      completed: [];
    });
  });
});

Black Box Testing Philosophy

Function, module, or class

input

output

Environment

side effect

Black Box Testing Philosophy

multiply

(2,3)

6

Environment

How Does A Component Fit the Black Box Model?

React Test Example

const AnimalPreview = (props) => (
  <MuiCard>
    <MuiCardImage src={prop.imageSource} variant={1} />
    <MuiCardTitle>{prop.name}</MuiCardTitle>
    <MuiCardText>{prop.summary}</MuiCardText>
    <MuiCardActions>
      <MuiButton onClick={prop.onShareClick}>Share</MuiButton>
      <MuiButton onLearMoreClick={prop.onLearnMoreClick}>
        Learn More
      </MuiButton>
    </MuiCardActions>
  </MuiCard>
);

AnimalPreview

MuiCard

MuiCardTitle

h1

MuiCardText

p

MuiCardImage

img

MuiCardActin

button

Button

Button

button

AnimalPreview

h1

p

img

button

button

Copy of deck

By Yazan Alaboudi