lundi 29 avril 2019

React-redux unit testing: testing a method inside class component that its only function is to dispatch an action to the store

I'm unit testing my book-list component in my React-redux app, basically it shows a list of saved books that the user has, by mapping an array of books into a BookCard component, which has two buttons: modify book and delete book. I've done all my tests successfully except for the delete book method, this method only dispatches an action to the store to delete a book from the state (haven't done the HTTP request to the backend functionality yet), the BookList component is a connected component. My problem is: I can't delete the BookCard from the Wrapper/Instance/MockStore, even though in the app itself it works. So I don't know if is excessive to test this button, because I've already tested the action and reducer for this purpose.

I've tried a lot of things, executing the deleteBook method from the wrapper with the mount() and shallow() functions (doing the respective dives() with shallow()) from enzyme, then testing if the selector for the BookCard exists (and yes, it still exists), then I tried to instance the component with shallow().instance(), but the same thing, it doesn't deletes. And finally I tried testing if the book was being deleted from the mockStore I created specially for that test (using store.getState()), and no, it didn't delete the book

// State and store 

let state = {
  dataState:
  {
    books: {
      'Random,Jane Doe':
      {
        name: 'Random',
        author: 'Jane Doe',
        keyTakeaways: [{ 'value': 'Nice!' }]
      }
   },
    users: {
      1: {
        userName: 'Jon Doe',
        email: 'jondoe@gmail.com'
      }
    }
  },
  uiState: {
    currentUser: 1
  }
};
let initialStore = configureMockStore(state);

// Test

it('Deletes books properly', () => {
      const name = state.dataState.books['Random,Jane Doe'].name;
      const author = state.dataState.books['Random,Jane Doe'].author;
      bookListWrapper = shallow(<BookList store={initialStore} />);
      bookListWrapper.dive().dive().instance().deleteBook(name, author);
      expect(initialStore.getState()).toEqual(true);
});

// Book Card component return statement

return <>
      <Card id={`card-${id}`} key={id} style=>
        <ButtonBase id={id} style= onClick={this.openDetails}>
          <div className={classes.content}>
            <div className={classes.info}>
              <div>
                <Typography className={classes.title} color="textSecondary">Book's name</Typography>
                <Typography className="book-name" color="textPrimary">{book.name}</Typography>
              </div>
              <div>
                <Typography className={classes.title} color="textSecondary">Author's name</Typography>
                <Typography className="author" color="textPrimary">{book.author}</Typography>
              </div>
            </div>
            <div>
              <Typography style= color='textSecondary'>Key Takeaways</Typography>
              <div className={classes.column}>
                { //Maps the books.keyTakeaways array to show each corresponding value
                  book.keyTakeaways.map((keyTakeaways, idx) => (
                    keyTakeaways.value !== ''
                      ? <p id={`key-takeaway-${idx}`} className={classes.list} key={idx}>{idx + 1}. {keyTakeaways.value}</p>
                  : <Typography key={idx} align="center" color="textPrimary">No learnings or notes for this book</Typography> // In case no Key Takeaways were added
                  ))
                }
              </div>
            </div>
          </div>
        </ButtonBase>
        <div style=>
          <button className={classes.button} id={`delete-${id}`} type="button" onClick={this.deleteBook}>Delete</button>
          <button className={classes.button} id={`modify-${id}`} type="button" onClick={this.modifyBook}>Modify</button>
        </div>
      </Card >
      {details}
    </>

// Delete book method from child (BookCard) component
deleteBook(event) {
    event.preventDefault();
    const { author, name } = this.state; 
    this.props.deleteBook(name, author); // Sends data to parent
}

// Delete book method from parent (BookList) component
deleteBook(name, author) {
    this.props.deleteBook(name, author); // Dispatch action to store
}

The results are that no card is being deleted from the store or component, when it should be because I'm executing the deleteBook method and dispatching the action to the store. Is worth mentioning that I'm using redux-mock-store package to create a mock store for the tests

Aucun commentaire:

Enregistrer un commentaire