-
Notifications
You must be signed in to change notification settings - Fork 4
Client Side Testing
JT edited this page Nov 26, 2016
·
16 revisions
- Jasmine: We chose Jasmine as our testing framework due to its simple syntax, and the fact that it is 100% standalone.
On a side-note Jasmine is a requirement to test the Angular2 framework because of the lack of hook libraries for other testing frameworks. Instead of adhearing to a differrnt testing framework for the server-side we believed it would be simpler to use Jasmine for all layers of the GOAT-stack.
- Karma: We chose Karma because it was made by Google and chosen by Google as a test-runner for Angular 2.
Because of our implementation of Redux for Angular2 only two files are responsible for client-side data handling, 'actions' and 'reducers'. Components should only be responsible for DOM event manipulation and/or hooks for the Redux store data to their respective templates. Which means Protractors e2e testing should be responsible for the rest.
- Actions:
- Primary assumptions to test for:
- is an action being called.
- was the action that was called the correct one.
- Primary assumptions to test for:
- Reducers:
- Primary assumptions to test for:
- Is the initial state correct
- Is the previous state correct
- Is the action correct
- Is the next state correct
- Primary assumptions to test for:
import { Injectable } from '@angular/core';
import { FormGroup, NgForm } from '@angular/forms';
import { NgRedux } from 'ng2-redux';
import { IAppState } from '../../store/index';
/////////////////////////////////////////////////////////////////////////
/* UserForm Actions: used to call dispatches to change the userForm
object in the store
LOGIN_FORM_IN -> Opens the login form
LOGIN_FORM_OUT -> Closes the Login form
*/
/////////////////////////////////////////////////////////////////////////
@Injectable()
export class UserFormActions {
constructor(private ngRedux: NgRedux<IAppState>) { }
static LOGIN_FORM_IN: string = 'LOGIN_FORM_IN';
static LOGIN_FORM_OUT: string = 'LOGIN_FORM_OUT';
loginForm(action: boolean) {
if (action)
this.ngRedux.dispatch({ type: UserFormActions.LOGIN_FORM_IN });
else
this.ngRedux.dispatch({ type: UserFormActions.LOGIN_FORM_OUT });
}
}import { NgRedux } from 'ng2-redux';
import { UserFormActions } from './userForm.actions';
class MockRedux extends NgRedux<any> {
constructor() {
super(null);
}
dispatch: () => {};
}
describe('UserForm Actions Creator', () => {
let actions: UserFormActions;
let mockRedux: NgRedux<any>;
beforeEach(() => {
mockRedux = new MockRedux();
actions = new UserFormActions(mockRedux);
});
it('should dispatch LOGIN_FORM_IN action', () => {
const expectedAction = {
type: UserFormActions.LOGIN_FORM_IN
};
spyOn(mockRedux, 'dispatch');
actions.loginForm(true);
expect(mockRedux.dispatch).toHaveBeenCalled();
expect(mockRedux.dispatch).toHaveBeenCalledWith(expectedAction);
});
it('should dispatch LOGIN_FORM_OUT action', () => {
const expectedAction = {
type: UserFormActions.LOGIN_FORM_OUT
};
spyOn(mockRedux, 'dispatch');
actions.loginForm(false);
expect(mockRedux.dispatch).toHaveBeenCalled();
expect(mockRedux.dispatch).toHaveBeenCalledWith(expectedAction);
});
});- click here to see the transformers file
- click here to see the types file
- click here to see the initial-state file
import { UserFormActions } from '../../actions/userForm/userForm.actions';
import { reimmutifyUserForm } from './userForm.transformers';
import { IUserForm } from './userForm.types';
import { INITIAL_STATE } from './userForm.initial-state';
// Define the reducer that will initiate state changes for userForm
export function userFormReducer(state: IUserForm = INITIAL_STATE, action: any) {
// will decide what state change is necessary based off the type
switch (action.type) {
case UserFormActions.LOGIN_FORM_IN:
return state
.updateIn(['userSigning'], val => true)
.updateIn(['userSignup'], val => false);
case UserFormActions.LOGIN_FORM_OUT:
return state
.updateIn(['userSignup'], val => false)
.updateIn(['userSigning'], val => false);
default:
return state;
}
}import { Map } from 'immutable';
import { userFormReducer } from './userForm.reducer';
import { INITIAL_STATE } from './userForm.initial-state';
import { UserFormActions } from '../../actions/userForm/userForm.actions';
describe('UserForm Reducer', () => {
let initialState = INITIAL_STATE;
beforeEach(() => {
initialState = userFormReducer(undefined, { type: 'TEST_INIT' });
});
it('should have an immutable initial state', () => {
expect(Map.isMap(initialState)).toBe(true);
});
it('should set userSigning to true on LOGIN_FORM_IN', () => {
const previousState = initialState;
const nextState = userFormReducer(previousState,
{ type: UserFormActions.LOGIN_FORM_IN });
expect(previousState.getIn(['userSigning'])).toBe(false);
expect(previousState.getIn(['userSignup'])).toBe(false);
expect(nextState.getIn(['userSigning'])).toBe(true);
expect(nextState.getIn(['userSignup'])).toBe(false);
});
it('should set userSigning to false on LOGIN_FORM_OUT', () => {
const previousState = userFormReducer(initialState,
{ type: UserFormActions.LOGIN_FORM_IN });
const nextState = userFormReducer(previousState,
{ type: UserFormActions.LOGIN_FORM_OUT });
expect(previousState.getIn(['userSigning'])).toBe(true);
expect(previousState.getIn(['userSignup'])).toBe(false);
expect(nextState.getIn(['userSigning'])).toBe(false);
expect(nextState.getIn(['userSignup'])).toBe(false);
});
it('should set swap userSignup and userSigning on LOGIN_FORM_IN', () => {
const previousState = userFormReducer(initialState,
{ type: UserFormActions.REGISTER_FORM_IN });
const nextState = userFormReducer(previousState,
{ type: UserFormActions.LOGIN_FORM_IN });
expect(previousState.getIn(['userSigning'])).toBe(false);
expect(previousState.getIn(['userSignup'])).toBe(true);
expect(nextState.getIn(['userSigning'])).toBe(true);
expect(nextState.getIn(['userSignup'])).toBe(false);
});
});