ng-redux icon indicating copy to clipboard operation
ng-redux copied to clipboard

Unit test component examples?

Open frankandrobot opened this issue 6 years ago • 3 comments

Have you guys had any luck unit testing components? (See for example, https://puigcerber.com/2016/02/07/how-to-test-angular-1-5-components/)

I've currently configured angular 1.5, ngtemplate-loader, and webpack to load the HTML partials correctly in karma but tests are erring out with the following ngRedux error:

	_iterator is undefined
	ngReduxProvider/this.$get@webpack:///~/ng-redux/lib/components/ngRedux.js:57:0 <- test.webpack.js:148525:85
	invoke@webpack:///~/angular/angular.js:4771:0 <- test.webpack.js:81782:16

Update: oh wait, I probably have to configure ngRedux in tests as well (doh)

frankandrobot avatar Dec 28 '17 05:12 frankandrobot

Let us know how that works out!

If you're testing redux, I'd make sure to do unit tests on the reducers themselves. It's easier but it's also way more robust.

AntJanus avatar Dec 28 '17 17:12 AntJanus

Yea i got this working. Unfortunately, there's no code to share because it's closed source :( but here's an overview of what needs to happen:

  1. First do you really need to unit test the template? In our case, the template (in particular, the underlying angular form) had a lot of state that needed to be tested. This is difficult to test in controller/reducer tests because we mainly let Angular forms do their own thing (even though we use redux and one-way data binding---the culprits here are validators).
  2. Use ngtemplate-loader and html-loader to make webpack load the templates. The ngtemplate-loader instructions worked.
  3. Setup ngRedux by creating a throwaway module in the tests
  4. Load ngRedux into the tests by using angular mock to load the throwaway module from step 3
  5. Load the component-under-test by using angular mock
  6. Proceed to test the template by following https://puigcerber.com/2016/02/07/how-to-test-angular-1-5-components/)

Here's a minimal test example that uses karma and mocha:

import 'angularMocks';
import angular from 'angular';
import 'ng-redux';

import appReducers from '../../reducers';
import './user-completion.component';
import '../../app';

// Setup angular. Note that we need a working ngRedux for the template tests
angular.module('testConnect', ['ngRedux']).config([
  '$ngReduxProvider',
  function($ngReduxProvider) {
    $ngReduxProvider.createStoreWith(appReducers, []);
  },
]);
describe.only('component', function() {
  let ngRedux;

  beforeEach(angular.mock.module('testConnect'));
  beforeEach(angular.mock.module('componentUnderTest'));
  beforeEach(angular.mock.inject(function($ngRedux) {
    ngRedux = $ngRedux;
  }));

  describe('with $compile', function() {
    let element;
    let scope;

    beforeEach(
      angular.mock.inject(function($rootScope, $compile) {
        scope = $rootScope.$new();
        element = angular.element('<component-under-test></component-under-test>');
        element = $compile(element)(scope);
        scope.$apply();
      })
    );

    it('should show missing username', function() {
      ngRedux.dispatch(updateState({
        missing: {
          username: true,
          email: false,
        },
      }));
      scope.$apply();
      const result = element.html().toString();
      expect(result).to.match(/Username/);
    });
  });
});

frankandrobot avatar Dec 28 '17 18:12 frankandrobot

One of the things that I noticed, however, is that it's not obvious how to do fuller integration tests i.e., a test where everything is real except for AJAX requests. In our code base we are already doing this when testing sagas. Unfortunately, for this to work we use redux-mock-store-middleware which allows a fully working store with getActions and resetActions functionality (needed for testing).

So... to make these types of integration tests work with templates, we need the ngRedux magic of reduxifying angular components and the redux-mock-store-middleware functionality. Hmm.... since it is middleware, I could probably still use it. However, our tests recreate the store on each test (that means we get a fresh copy of the redux state each test)... I don't see an obvious way of doing that (meaning tests won't be independent).

Update: I suppose there's this: https://stackoverflow.com/questions/35622588/how-to-reset-the-state-of-a-redux-store/35641992#35641992 The downside is that tests start to get a lot of boilerplate

frankandrobot avatar Dec 28 '17 21:12 frankandrobot