Skip to content

Instantly share code, notes, and snippets.

@iammerrick
Created December 16, 2015 21:50
Show Gist options
  • Save iammerrick/49b4420d64f954fa6c5e to your computer and use it in GitHub Desktop.
Save iammerrick/49b4420d64f954fa6c5e to your computer and use it in GitHub Desktop.
import expect from 'expect';
import AppConstants from '../../../app/AppConstants';
import store from '../../store';
import { selectLastReadMessageByChannelId } from './channelSelectors';
describe('channel', () => {
it('should store the messageId when clearing unreads', () => {
const channelId = 1;
const messageId = 2;
store.dispatch({
type: AppConstants.CHANNEL_UNREADS_CLEARED,
payload: {
id: channelId,
messageId
}
});
expect(selectLastReadMessageByChannelId(store.getState(), channelId)).toEqual(messageId);
});
});
@iammerrick
Copy link
Author

The upside this test is more likely to tell you "something broke" not "what broke". I think optimizing tests for debug time comes at too high of a cost as test authoring time. Thoughts?

@statianzo
Copy link

I think it's an interesting approach. Reducers and selectors are already implicitly coupled through knowing the structure of the state tree they work on.

@statianzo
Copy link

On that note, this actually decouples the test from the state tree, providing flexibility in reorganizing your structure if desired.

@trevordmiller
Copy link

IMO, this is great for ensuring everything is working; I also like to test the individual underlying pieces so that when the tests breaks there is a chain to what exactly has broken: I have been layering tests in pieces all the way up to this sort of "integration" test as you have here. Like:

  • Store
    • Complete integration test with manual dispatching as you've done here to make sure the starting state + a bunch of actions = expected ending state.
  • Feature
    • Action creator test
    • Reducer test
    • [Selector test]
  • Feature
    • etc.

Full example of this from the repo I am using for my Egghead.io "React Testing Cookbook" in the works: https://github.com/trevordmiller/favorite-quotes

May be overkill but it makes me feel warm and fuzzy ;) haha

@iammerrick
Copy link
Author

@statianzo Agreed. Now your tests don't have to know about the structure of your state tree. I think one addition to this would be making a custom createStore with middleware and creating the store in beforeEach to not be using a singleton store.

@iammerrick
Copy link
Author

:-) I think your approach definitely has the most coverage but at a relatively high cost. Your implementation is all bolted down by your tests right?

Meaning, if you decided to change your states structure, you have to change your selector, selectorTest, reducer, reducerTest.

@iammerrick
Copy link
Author

I'm interested in testing as a "How do we catch bugs before our customers do with reasonable effort" more than testing as "100% confidence this thing is work" because I think usually that 100% confidence is a false sense of safety and comes at a high cost of test maintenance and authoring.

@mikeyamadeo
Copy link

Is the idea behind this to avoid writing the same logic to select a piece of state for, let's say, each react component that needs the last read message?

@trevordmiller
Copy link

@iammerrick: Good point. This does seem to be a good lightweight approach to ensure the core functionality is working and still let you refactor easily.

@trevordmiller
Copy link

@iammerrick: I say try it out and see if it works well - if it does, share it with the community!

@statianzo
Copy link

It's similar to an approach I've tried to take in action creators that use getState, using selectors to avoid something else dependent on the state tree

@statianzo
Copy link

👍 to creating store in a beforeEach.

@iammerrick
Copy link
Author

Alright thanks for feedback all, just wanted to make sure I wasn't doing anything crazy.

@kloy
Copy link

kloy commented Dec 17, 2015

I have been using a similar approach for a while, except I utilize the action creators. My theory is that the isolation of tests as demonstrated in the docs does not give you must value in real applications. In real applications change is what introduces risk, atomic tests will not protect code well against change. You gain more value by testing that a given action creator results in an expected state. By explicitly passing action objects to a reducer you are testing the implementation, not the behavior.

This approach has already saved me several times as new requirements are introduced/discovered. Now when I change the implementation of an action, my asserts on state fail.

Here are two examples, one sync and one async.

https://gist.github.com/kloy/3d0dafe1f6974f88c909
https://gist.github.com/kloy/ef9e99ae67df24e68bdd

I still have not optimized my http mocking as I would prefer to work closer to angular's $http. As you can see I am utilizing a mock store. My mock store pulls in all of my reducers and all middleware that is not depending on globals/externals.

@ryanflorence
Copy link

if you decided to change [x], you have to change [y]

Apps always change. For me, about the only thing I care about is that.

My favorite code allows me to change one thing w/o having to change many things.

For example, I don't assert an exact DOM structure for UI tests, I just assert that the user's name is displayed: I don't care if its in a <b/> or <span/>. You've got the same thing going on here by not coupling your tests to your state tree, you're testing "does this work" not "how does this work" and its 💯

@ryanflorence
Copy link

(there are times I look at how simple my reducers--and everything else in a react redux app--are to think about and wonder why I'm even writing tests at all ...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment