Coder Social home page Coder Social logo

testing about angular-couch-potato HOT 22 OPEN

laurelnaiad avatar laurelnaiad commented on June 1, 2024
testing

from angular-couch-potato.

Comments (22)

andre-campos avatar andre-campos commented on June 1, 2024

temp
Hi @stu-salsbury. I'm no angular expert here (I'm just starting to play with it, actually), and I'm trying to set up a project the proper way. I've purchased an angular template that uses couchpotato with requirejs to lazy load all the modules. And that seems to work pretty well. However the template has zero tests and I'd like my app to follow the good practices related to testing angular apps.

I've managed my tests to run; I can inject a module into the tests, but I can't get ahold of the controllers defined within the module because they're lazy-loaded. Here's an example:

I've got an auth module that handles login,registration, etc:

define([
    'angular',
    'angular-couch-potato',
    'angular-ui-router'
], function (ng, couchPotato) {

    "use strict";

    var module = ng.module('app.auth', [
        'ui.router'
    ]);

    couchPotato.configureApp(module);

    var authKeys = {
    };

    module.config(function ($stateProvider, $couchPotatoProvider) {
        $stateProvider.state('login', {
            url: '/login',
            views: {
                root: {
                    templateUrl: 'app/auth/views/login.html',
                    controller: 'LoginCtrl',
                    resolve: {
                        deps: $couchPotatoProvider.resolveDependencies([
                            'auth/services/UserService',
                            // 'auth/directives/loginInfo',
                            'auth/login/LoginCtrl'
                        ])
                    }
                }
            },
            data: {
                title: 'Login',
                htmlId: 'extr-page',
                insecure: true
            },
            resolve: {
                deps: $couchPotatoProvider.resolveDependencies([
                    'modules/forms/directives/validate/smartValidateForm'
                ])
            }
        })
    }).constant('authKeys', authKeys);

    module.run(function($couchPotato){
        module.lazy = $couchPotato;
    });
    return module;
});

My login controller is simple enough (please don't mind the pt-BR):

define(['auth/module'], function (module) {

    "use strict";

    return module.registerController('LoginCtrl', function ($scope, $state, $http, UserService) {

        $scope.email = null;
        $scope.senha = null;
        $scope.rememberMe = null;

        $scope.erroLogin = "erro";

        $scope.submit = function(){
            UserService.login($scope.email, $scope.senha, $scope.rememberMe).then(
                function(sucesso){
                    $state.go('app.dashboard');
                },
                function(erro){
                    if (erro.status == 401){
                        $scope.erroLogin = 'Credenciais inválidas. Por favor, tente novamente';
                    }
                    else{
                        $scope.erroLogin = 'Erro desconhecido. Por favor, tente novamente.';
                    }
                }
            );
        };
    })
});

And finally, I've got a test method that I'd like to test the controller with:

define(['angular', 'angular-ui-router', 'angular.mock',  'angular-couch-potato', 'auth/services/UserService', 'auth/login/LoginCtrl'], 
    function( angular, router, angularMocks, couchpotato, service, LoginCtrl) {

    describe('LoginCtrlTest', function(){

        var $q, $rootScope, $state, $http, controller;

        beforeEach(angular.mock.inject(function ($injector) {
            $q = $injector.get('$q');
            $rootScope = $injector.get('$rootScope');
            $http = $injector.get('$http');
            controller = LoginCtrl;
            controller.run();
            $rootScope.$digest();
        }));

        it('The controller should exist', function() {
             expect(controller).not.toBe(null);
        });

        it('It should be able to login', function() {
            console.log($rootScope);
            console.log(controller);
            expect($rootScope).not.toBe(null);
        });
    })
});

I know, it doesn't really test anything. But I'm just trying to get ahold of my controller at this point. If you could shed me some light on how to do this properly, I'd really appreciate it. Cheers.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

Hi @andre-campos .

So you're "no expert" at Angular, but youi're lazy loading controllers using requirejs and wanting to test them. You have your work cut out for you! :)

This looks like it may be the first sign of trouble:

define(['angular', 'angular-ui-router', 'angular.mock',  'angular-couch-potato', 'auth/services/UserService', 'auth/login/LoginCtrl'], 
    function( angular, router, angularMocks, couchpotato, service, LoginCtrl) {

When I see that I wonder what's in your require.js config. There are some very specific dependencies that you'll need to set up bewteen the modules listed above if you want this to work. The fancy way to set it up is to have your specific component modules depend on your base angular module (you've done that), and then to create an "uber" module that itself drags in all of the components of your application (and perhaps does the angular app's .config function. Of course there are a million ways to do this.

I have two questions for you, to start: (a) what's your require.config and (b) what specifically indicates that there is a failure (i.e. an error message or your machine turns an awful shade of green or something)....

Also, you sometimes need to provide at least a "dummy" template file when you hook a controller up to a scope, so be prepared to give that template to your tests through the mock $httpBackend mechanism if they're needed...

from angular-couch-potato.

andre-campos avatar andre-campos commented on June 1, 2024

Thanks the quick reply @stu-salsbury. What can I say, eh? Go big or go home! ;) I will create a mini version of my project and isolate only the module in question. I will add that to my github; that should show you exactly where I'm at. I will get back to you in a bit. Cheers

from angular-couch-potato.

andre-campos avatar andre-campos commented on June 1, 2024

Alright, kind sir @stu-salsbury, I've created a test project with my stripped down setup. Here's the link: https://github.com/andre-campos/angular-requirejs-couchpotato-tests

Your help is truly appreciated. Cheers

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

I will take a look when I get a moment. It's been a while since I used either grunt or karma, but I think I can hack it. :)

from angular-couch-potato.

andre-campos avatar andre-campos commented on June 1, 2024

Hey @stu-salsbury did you end up looking into this at all? Cheers

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

Hi @andre-campos -- slight change of github user name... still me. I have been buried lately and I just found myself with a moment to look at this.

Unfortunately, I'm kind of stuck at the gate -- when I try to run grunt karma I see this:

Running "karma:unit" (karma) task
INFO [karma]: Karma v0.12.28 server started at http://localhost:9876/
INFO [launcher]: Starting browser Firefox
WARN [watcher]: Pattern "/Users/ssalsbur/Documents/_ws/angular-requirejs-couchpotato-tests/public/smartadmin-plugin/**/*.js" does not match any file.
INFO [Firefox 35.0.0 (Mac OS X 10.10)]: Connected on socket VM-Nry6kPgWcdRRblQIq with id 65450206
Firefox 35.0.0 (Mac OS X 10.10): Executed 2 of 2 SUCCESS (0.01 secs / 0.008 secs)

Note: Chrome started giving me funny businss when I tried to run your tests, so I switched the config to Firefox.

I find myself unsure of what's going on with the attempt to load your tests. I assumed I'd see a failing test, but if I'm just getting 2 of SUCCESS do you think it's even running the test you're having trouble with? I think there was a time wheI sort of understood how to configure karma, but those days are hazy now. To be honest, one of the original reasons I switched to mocha was because configuring it to work with require.js just made a lot more sense to me than what happened when I tried to do with karma. Do you want to try cloning your repo to a new directory locally to make sure it's configured to run the test we're trying to debug (i.e. no path issues or missing files)? If yo can point me to an actual failing test, as well as remind me how t get into debug mode in the tests, that would probably get us on track to track it down.

from angular-couch-potato.

JiaqingGU avatar JiaqingGU commented on June 1, 2024

Hi,
I am recenly working on the angular project with couch potato. And I am struggling for a few weeks to make unit test running with couch potato, however I don't make big progress till now.
I looked through your post here: afterglowtech/angular-couchPotato#2
Seems you did some changes to make couch potato woring with unit test. Would you please offer me some examples or guides how to create unit test with couchPotato?

Thans in advance.

Jiaqing

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

Perhaps it makes sense for me to (finally) add some unit tests to the repo.

P.S. @JiaqingGU the link included is to an older version of the repo, just so you know. The code here, https://github.com/laurelnaiad/angular-couch-potato/ is slightly more recent.

from angular-couch-potato.

JiaqingGU avatar JiaqingGU commented on June 1, 2024

Hi,
Thank you for your quick response. That would be really great if you could do that.
Additionally, if I use "bower install angular-couch-potato", it always try to fectch the code from git://github.com/stu-salsbury/angular-couch-potato.git instead of https://github.com/laurelnaiad/angular-couch-potato.git with version 0.1.1. But it seems the latest versoin is 0.2.0.
I am not quite familier with this version and repo stuff. But I just find out in the bower.json, version is 0.1.1, and in package.json version is 0.2.0. And both url points to git://github.com/stu-salsbury/angular-couch-potato.git
I am just confused. I would really appreciate if you could explain a bit how I could get the latest vesion for couch-potato.
Many thanks.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

Thank you for the reminder about bower. I've updated it. The current version is now 0.2.3, make sure you get that one. laurelnaiad is the repo.

from angular-couch-potato.

jorisvddonk avatar jorisvddonk commented on June 1, 2024

I think I've found a way to make unit tests work: by making your RequireJS modules (e.g. the services/controllers you create) return arrays and getting instances of the required services/components via $injector.invoke(...):

In path/to/myService.js:

define(['app'], function(app){
    "use strict";

    var serviceDefinition = ['$q', function($q) {
        var mValue = "Some value for this example service";

        return {
            setValue: function(val) {
                mValue = val;
            },
            getValue: function() {
                return mValue;
            },
            getValueAsPromise: function() {
                var deferred = $q.defer();
                deferred.resolve(mValue);
                return deferred.promise;
            }
        };

    }];

    app.registerService('myService', serviceDefinition); // works also with app.service('myService', serviceDefinition)
    return serviceDefinition;
})

In test.js:

define(['angular', 'app', ..., 'path/to/myService'], function(angular, app, ..., myServiceDefinition) {
    describe('myService', function() {
      var myService;
      beforeEach(angular.mock.module({'app': app}));
      beforeEach(angular.mock.inject(function ($injector) {
        myService = $injector.invoke(myServiceDefinition);
      }));
      it ('should do something', function() {
        //Test myService here
      });
    });
});

Haven't had enough time yet to figure out if this covers all cases and works properly in all situations, but at least I seem to be able to create some tests using this mechanism, rather than having no luck at all as was the case earlier.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

@jorisvddonk thank you for posting that. It looks reasonable.

Is there a reason you used $injector.invoke instead of angular mocks' inject function? I should hope that either way would work.

from angular-couch-potato.

jorisvddonk avatar jorisvddonk commented on June 1, 2024

Using angular.mock.inject doesn't work, since that function uses the name of the arguments passed onto it to resolve dependencies, and once it tries to resolve these by name you get the dreaded Unknown Provider error because Angular hasn't actually been able to load the service/controller yet. What we want to do is pass an array describing a service/controller so that we can then instantiate it and run our tests on it, essentially defining it manually. That's why $injector.invoke needs to be used.

One thing: I'm currently trying to get dynamically loaded dependencies working, so that it's possible to test services depending on other dynamically loaded services, or so that it's possible to test directives. So far I haven't had much luck, but I only just started trying to get tests working for directives after having succesfully written a few tests for simple services and controllers.

One alternative to all of this would be to disable lazy loading altogether during testing, e.g. by compiling everything into a single JavaScript file bundle via something like r.js. That may be easier, but I don't think that's ideal as you probably get awful stacktraces that way (assuming the optimizer minifies as well; I don't really know) and you'll find that you might actually be testing different code altogether if you do something wrong.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

My last comment isn't what I meant to say at all. But I'm awake now (or at least closer to awake). 😄

I'd definitely not be satisfied with disabling lazy loading -- couch potato's functionality itself needs to be testable, as well as apps written using it, for me to even claim this to be a ready-for-use utility....

Your test above doesn't really make use of Couch Potato or exercise its code much.

Keep in mind that couch-potato's real purpose in life is to set you up with components you can load via its resolve, resolveDependencies and resolveDependenciesProperty functions. If your app is really using couch-potato, you'll want to know that you can define components using couch-potato's register functions, and then load them via its three resolve functions.

In the typical use case, couch-potato's resolve functions are invoked during a routing transition so that the dependencies are injectable end wind up being injected to a view controller constructor. In a unit test, you'll want to inject them more manually using one of those resolve functions explicitly.

The resolve functions do two things -- (1) they cause the component to be loaded into angular's injector table as a side-effect and (2) they return a list of injectable component names so that the injector, while calling the controller constructor, injects them to that constructor. The reason that there are three is that in the typical routing use cases there are a couple of sugary features added to nake sure the router gets what it expects as the definition of what to inject to the view controller.

In a unit test scenario, you would probably set up some sort of spy which acts as the controller (though it need not be an actual), then you'd use one of the resolve functions to put the couch-potato dependency(ies) into the injector definitions, and you'd grab the result of that function and append it to the array of injected parameters for your pseudo-controller's constructor, then you'd proceed "as normal" with your unit test of the components from within that pseudo-controller (which can really just be any old factory).

from angular-couch-potato.

jorisvddonk avatar jorisvddonk commented on June 1, 2024

Hmm, you're absolutely right. My code above essentially bypasses couch-potato and makes RequireJS as well as the writer of the test do all the hard work to get things working. This is trivial for simple services and controllers, but it quickly becomes cumbersome when trying to write tests for lazy loaded components which themselves depend on other lazy loaded components, as the test author would have to write code to load and inject all of the dependencies. Ideally, couch-potato would make it easier for test authors to do their jobs, rather than making it more difficult.

I'll have to look at the various resolve functions; perhaps using those is indeed the proper solution here.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

Ideally, couch-potato would make it easier for test authors to do their jobs, rather than making it more difficult.

This is a good point. I'm pretty sure that, such as it is, my above description of what would be a valid testing methodology is accurate, but it's possible that there may be room for a test helper function (or perhaps a custom harness component) being written in order to reduces the lines-of-code required to exercise couch potato -- essentially, a generic factory that one could inject into tests which is configured to easily load couch potato components. It's odd but true that while their sugar is very useful in routing scenarios, in testing scenarios it gets "wordy"/verbose.

from angular-couch-potato.

laurelnaiad avatar laurelnaiad commented on June 1, 2024

perhaps using those [resolve functions] is indeed the proper solution here

Yes, those functions are the "meat" of couch-potato at runtime, while the "registerXYZ" functions comprise its fundamental config-time surface area.

from angular-couch-potato.

jorisvddonk avatar jorisvddonk commented on June 1, 2024

I've tried using the resolveDependencies function, but it doesn't seem to work, though I'm not exactly sure I'm calling it correctly, and I'm sure that what I'm writing here isn't what one would eventually like to see in a unit test:

    beforeEach(function(done){angular.mock.inject(function ($injector, $couchPotato) {
      var delayed = $couchPotato.resolveDependencies(['some/angularjs/service']);
      var promis = $injector.invoke(delayed);
      promis.then(function(resolv){
        done();
      });
    })});

This errors out with the dreaded $digest already in progress error as soon as you try to actually do anything in your test (like flush the httpBackend); the promise does get resolved though. Fixing this may be relatively easy, but it's (luckily!) been too long since I've had to deal with these kind of errors in AngularJS, so I'll have to read up again on how this works exactly.

from angular-couch-potato.

jeriko avatar jeriko commented on June 1, 2024

Did you figure this out? I'm in the same situation with the aforementioned angular/requirejs/couchpotato theme.

from angular-couch-potato.

jorisvddonk avatar jorisvddonk commented on June 1, 2024

Nope. I decided to rip out CouchPotato and RequireJS because I felt that the added functionality (though amazingly cool) did not warrant the added complexity. We were bringing in a Junior engineer at the time whom would mostly work on the project making use of that theme, but she had relatively little experience with Angular and RequireJS, and I didn't feel like putting the burden of said complexity on her as well.

The theme in question has some other problems as well (awful folder/file structure, weird build process output, out of date package.json/bower.json, dependency issues), and I must say that getting rid of the cruft was (in retrospect) the best decision I could make. If you're good with macros of your editor of choice, refactoring and fixing the entire thing may take about a day.

from angular-couch-potato.

jeriko avatar jeriko commented on June 1, 2024

Thanks for responding. I came to the same conclusion - the complexity doesn't seem worth the benefits in this case. I've almost finished removing CouchPotato from the theme, but am hoping to leave RequireJS. As you say, that theme definitely has some strange quirks, today is dedicated to refactoring it into something more sane :)

from angular-couch-potato.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.