Giter VIP home page Giter VIP logo

Comments (41)

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

Hey @firehist where does this error appear? In browser console, or is it an http server which is served by ng-boilerplate and the error appears in your terminal?

from angular-translate.

knalli avatar knalli commented on May 13, 2024

@firehist you should provide more details about your test suite/code because the error message already told you: your test case does not work as expected. Either you setup is not correct (the requests should be expected) or your case is not correct (the request should not be fired). Personally, I would guess first option.

from angular-translate.

firehist avatar firehist commented on May 13, 2024

@PascalPrecht and @knalli Thank you for your try to answer even if it's not necessar directly related to ngTranslate.

  1. This error appear in the http server which is served by ng-boilerplate console when I launch karma:continuous grunt task (launch karma test with this configuration: https://github.com/joshdmiller/ng-boilerplate/blob/master/karma/karma-unit.js)
  2. More details

FROM

// Some code
.config( function myAppConfig ( $routeProvider ) {
    $routeProvider.otherwise({ redirectTo: '/home' });
})
// Some code

TO

// Some code
.config( function myAppConfig ( $routeProvider ) {
    $routeProvider.otherwise({ redirectTo: '/home' });
    /** Start edit **/
    $translateProvider.registerLoader({
        type: 'static-files',
        prefix: 'i18n/',
        suffix: '.json'
    });
    $translateProvider.uses('fr_FR');
    /** End edit **/
})
// Some code

My question is how to test that the translateProvider try to catch the right url/file on the server. I tried a lot of different configuration inspired examples of different angular docs: http://docs.angularjs.org/api/ngMock.$httpBackend

I hope that all this informations can help you to helping me :D

Have a good day and again thanks for all!

from angular-translate.

knalli avatar knalli commented on May 13, 2024

Have a look at https://github.com/PascalPrecht/ng-translate/blob/02b488a1e356212ba0a4ab502bfd8be920a0d277/test/unit/translateSpec.js#L738

from angular-translate.

firehist avatar firehist commented on May 13, 2024

@knalli Thanks. I already took a look to this file but the error "Error: Unexpected request: GET i18n/fr_FR.json" was still there.
I'll try again and report here updates and results

from angular-translate.

knalli avatar knalli commented on May 13, 2024

The links points to our test setup where httpBackend mock is instrumented correctly.

from angular-translate.

firehist avatar firehist commented on May 13, 2024

Hey guys!

Thanks again for your responses.

I removed

$translateProvider.uses('fr_FR');

from my app.js and its works great.

Thank you!

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

I'd like to suggest re-opening this issue. I think the root problem wasn't addressed.

It seems to me that the core problem is that if you register some loader in your app module's config method, then it will make the http call to grab the json strings file upon app module initialization. Which is fine for the actual app. But when trying to do unit testing, as soon as you try to angular.mock.inject anything, anywhere, it will initialize the app and throw the error mentioned above: Error: Unexpected request: GET foo.json.

It seems to be a chicken and egg problem because you can't use $httpBackend.when('GET', 'foo.json'... because to even inject $httpBackend requires the app module to be initialized beforehand.

The only solution I've read about is to break out all your app "components" (i.e. services, filter, directives, controllers) into a different module from the main app module. Then during unit testing you only load the child modules and not the parent where the app initialization code (setting up of $translateProvider) happens. Seems sort of awkward to me but it is doable.

Any other ideas? I'm not sure whether this is even an angular-translate problem, it may be more of an app module fundamental problem, but it seems one that needs solving if you want to unit test an app using angular-translate.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft hm.. looks like one has to mock the loader away.

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

I've been struggling with this for days. If you can provide an example, that would be a huge help.

from angular-translate.

knalli avatar knalli commented on May 13, 2024

I've provided a link, scroll a little bit upwards. It's in the test sources of the module itself.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft Maybe a more updated snippet would help out more: https://github.com/PascalPrecht/angular-translate/blob/master/test/unit/service/translate.spec.js#L439

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

Thanks @knalli and @PascalPrecht. But I think I'm still missing something. To illustrate the problem I'm having (and the problem it seems that everybody who uses angular-translate would have), I've published a simple example here:

https://github.com/marplesoft/unitTestingExample

You'll find a simple app that uses angular-translate with the static file loader and has one controller and one service. The app works fine. I've also written two unit tests which I run under karma/jasmine. You'll notice that I've employed your suggested approach to grab the $translateProvider and in my case, call translations(...) to provide mock data. This prevents the static file loader from kicking in and allows my unit test of the translations to work.

However, you'll notice that my other unit test of my service fails with the following error:

Chrome 30.0.1599 (Mac OS X 10.8.5) additionService should add numbers correctly FAILED
    Error: Unexpected request: GET strings-en.json
    No more request expected
        at Error (<anonymous>)
        at $httpBackend (/Users/ryan/Dev/unitTestingExample/app/lib/angular-mocks.js:959:9)
        at sendReq (/Users/ryan/Dev/unitTestingExample/app/lib/angular.js:9333:9)
        at $http (/Users/ryan/Dev/unitTestingExample/app/lib/angular.js:9124:17)
        at /Users/ryan/Dev/unitTestingExample/app/lib/angular-translate-loader-static-files.js:10:7
        at loadAsync (/Users/ryan/Dev/unitTestingExample/app/lib/angular-translate.js:186:40)
        at Function.$translate.uses (/Users/ryan/Dev/unitTestingExample/app/lib/angular-translate.js:286:13)
        at /Users/ryan/Dev/unitTestingExample/app/lib/angular-translate.js:21:18
        at Object.invoke (/Users/ryan/Dev/unitTestingExample/app/lib/angular.js:2990:25)
        at /Users/ryan/Dev/unitTestingExample/app/lib/angular.js:2842:71
Chrome 30.0.1599 (Mac OS X 10.8.5): Executed 2 of 2 (1 FAILED) (0.648 secs / 0.03 secs)

I could do the same mocking of the translation loader here but that would mean that in every unit test suite I write - could be 20, 50, 100 of them - regardless of whether the code under test has anything at all to do with angular-translate, I'd have to mock the translate stuff. Which seems really onerous to me.

Either I'm missing a best practice here or something is sort of broken about this, no?

p.s. I'm not the only one with this problem, see here:
http://stackoverflow.com/questions/17345928/how-do-i-test-controllers-with-angular-translate-initialized-in-app-config
http://stackoverflow.com/questions/18876290/how-do-unit-test-with-angular-translate

from angular-translate.

DWand avatar DWand commented on May 13, 2024

Hi, @marplesoft. I am not familiar with testing well (or, even, "at all"). However, I'd like to say that there are some possible ways to go.

At first, just like is said in the one of your links, one of the approaches is to break your application down into the modules. Such way, you could easily test your services which not are not dependent of translations.

Another approach is to mock the loader. No matter how: by using translations(...) method or by providing a custom loader which is not dependent of $http. This way, it's not necessary to deal with $httpBackend all the time.

But, yeah, I agree that these looks strange (or, even, mysterious). To be honest, I'm really surprised of such weird behavior of $httpBackend. As for me, the only logical way to deal with such kind of tests is to use $httpBackend to auto-respond all requests. So, if you'll find a reason why this approach doesn't work and a way how to solve this issue, please, leave a message here - I'd like to know too )

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

Hi @DWand - appreciate the thoughts.

For your first suggestion, sure I could break out some services and controllers that don't explicitly use $translate to their own module. Just seems like a lot of restructuring to be able to do testing.

In regards to mocking the loader, yes that's what I've done in my example. It does work, but would require me to repeat that for every single unit test suite in the project regardless of whether it uses $translate or not.

As I said earlier, I'm not so much blaming angular-translate for doing anything wrong. I'm not sure where the blame really lies, perhaps in the implementation of angular-mocks or a more fundamental aspect of angular's design. In any case, I can't imagine that I'm the first person to ever deal with this problem and there's got to be some kind of solution.

Maybe there's a way to initialize the module once, mock the translate loader, then use that module instance for all test suites?

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft @DWand @knalli Okay, so it turned out that it's pretty hard (or probably not even possible) to test angular apps that make use of angular-translate. The problem is clear. As soon as a module configuration uses a loader, it gets invoked as soon as $translate service gets instantiated the first time.

When writing unit tests, this is indeed a chicken and egg situation, since the XHR (if the loader does one) gets called implicit rather than explicit. Which means one doesn't have any control over the execution of the loader since it just depends on the first instantiation of $translate service.

To be honest, I haven't written an app that uses an asynchronous loader yet, so I also didn't need to write any kind of tests for such case. But I see the problem. Once you load your app module into your tests via beforeEach(module('myApp')); and that particular module declares the usage of an asynchronous loader that makes an XHR, there's no way around to stop the loader from being invoked at least when until the first inject() call within a spec block.

I'm not sure, but I wonder if it is possible to do something like:

beforeEach(module('myApp', function ($provide, $translateProvider) {
    // we're now within config() phase

    // let's build a fake loader
    $provide.factory('customLoader', function () {
       // loader logic goes here
    });

    // maybe this would override the loader configuration from `myApp` ?
    $translateProvider.useLoader('customLoader');
}));

it('should do sth.', function () {
    inject(function ($translate) {
        // $translate service gets injected the first time, and loader is called
        // I wonder if now `customLoader` is called, or if its still the one configured
        // in myApp.
    });
});

@marplesoft would be great if you could test this. If this work, we would have finally a working solution.

Otherwise, we probably have to introduce a new method that kicks of an asynchronous loader explicitly.

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

@PascalPrecht Yep it's becoming clearer now. Your suggestion would probably work for that one test suite, but angular mocks appears to tear down the injector and module after every test. So it doesn't look like you can hold any state in the $translateProvider from one test to the next. It seems like a non-starter to be able to unit test without doing some app restructuring.

One approach I can think of to solve this is to have the test runner exclude the app module's config function and instead have an alternate config function implementation for testing. In the later, you could declare some other method of loading translation data (either just use in-line .translations(...) or a mocked loader). The downside though is that you need to maintain two different config implementations and thus risk getting them out of sync and wasting time chasing down test errors.

I might try this out today and post an example for feedback if I can get it working.

from angular-translate.

knalli avatar knalli commented on May 13, 2024

Well, we have not a working test coverage for AngularJS in our projects yet. In context of the usage of $translate, we either mock it completely with an empty object (because not used in a non-fully coverage test) or it is configured separately with a custom loader.*

  • I would say that is not an issue. Because, you do not have to verify that the loader itself works ("Do not test the framework" mantra) but only that any loader works.. i.e. use a dedicated callback function.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft What's the problem with a after-every-test tear down of the module, as long as your module overrides a the used loader? This should actually be the solution. That's why things are wrapped in a beforeEach(). "Before each spec, please make sure to use the custom loader, which does NOT load translations asynchronously from a remote server."

So you actually don't want to hold a state from one test to another, instead what you actually want is to setup a fresh new environment for each spec aka each it() block. So if the code above overrides myApp module configuration for the test environment for each test, this is exactly what we want. Have you already tested it?

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

Yeah what you suggest is probably the only way to go. I still don't like having to spend cpu cycles setting up mocks for objects that aren't used - hundreds of times - when running my test suite but the perf cost probably isn't really that bad.

The point I was stuck on initially was how to avoid having to repeat that loader mocking code for every test but the solution is probably obvious: create a helper function that does module setup and call that rather than calling the module(...) function directly in your beforeEach methods.

I'll work on updating my example project with this approach and then post it here.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft Maybe I get you wrong, but just doing

beforeEach(module('youApp', function config(provider1, provider2) {

}));

Should already do the trick. There's no helper function needed.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@marplesoft regarding the perf. This shouldn't be a problem. Karma is really fast. angular-translate currently has around 300 tests. They all get executed within 1 sec.. Pretty sure you won't waste time :)

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

So in that example, what is provider1? Is that what mocks the loader?

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

I created a branch of that example project that uses the helper function I mentioned. It seems to work fairly well. See what you think.

https://github.com/marplesoft/unitTestingExample/tree/moduleHelper

In particular, see the following files:
Helper function that sets up in-line translations (either default or a map passed in: Code
Use of the helper function in a unit test that does a sanity check of the translation system: Code
Use of the helper function in a unit test that has nothing to do with translation - just uses default (empty) translations: Code

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

Yeap perfect.
On Oct 11, 2013 7:20 PM, "marplesoft" [email protected] wrote:

I created a branch of that example project that uses the helper function I
mentioned. It seems to work fairly well. See what you think.

https://github.com/marplesoft/unitTestingExample/tree/moduleHelper

In particular, see the following files:
Helper function that sets up in-line translations (either default or a map
passed in: Codehttps://github.com/marplesoft/unitTestingExample/blob/moduleHelper/test/helpers/moduleHelper.js
Use of the helper function in a unit test that does a sanity check of the
translation system: Codehttps://github.com/marplesoft/unitTestingExample/blob/moduleHelper/test/translationSpec.js
Use of the helper function in a unit test that has nothing to do with
translation - just uses default (empty) translations: Codehttps://github.com/marplesoft/unitTestingExample/blob/moduleHelper/test/additionServiceSpec.js


Reply to this email directly or view it on GitHubhttps://github.com//issues/42#issuecomment-26154708
.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

Could you, just for testing purposes, change your helper to use a custom loader which doesn't make an XHR?

from angular-translate.

marplesoft avatar marplesoft commented on May 13, 2024

I couldn't get that to work for some reason. Here's my helper:

'use strict';

function myappModule(translations) {
    var DEFAULT_LANG = 'en';
    var DEFAULT_TRANSLATIONS = {};

    module('myapp', function config($provide, $translateProvider) {
        $provide.factory('translateLoaderMock', function($q) {
            return function loaderFn(options) {
                var deferred = $q.defer();
                return deferred.resolve(options);
            }
        });

        $translateProvider.useLoader('translateLoaderMock', translations || 
                                                            DEFAULT_TRANSLATIONS);
        $translateProvider.preferredLanguage(DEFAULT_LANG);
    });

}

Tests failed with:

TypeError: $injector.get(...)(...) is undefined in /Users/ryan/exampleApp/app/bower_components/angular-translate/angular-translate.js (line 186)
    loadAsync@/Users/ryan/exampleApp/app/bower_components/angular-translate/angular-translate.js:186
    $translate.uses@/Users/ryan/exampleApp/app/bower_components/angular-translate/angular-translate.js:286
    @/Users/ryan/exampleApp/app/bower_components/angular-translate/angular-translate.js:21
    invoke@/Users/ryan/exampleApp/app/bower_components/angular/angular.js:2990
    createInjector/<@/Users/ryan/exampleApp/app/bower_components/angular/angular.js:2842
    forEach@/Users/ryan/exampleApp/app/bower_components/angular/angular.js:130
    createInjector@/Users/ryan/exampleApp/app/bower_components/angular/angular.js:2842
    workFn@/Users/ryan/exampleApp/app/bower_components/angular-mocks/angular-mocks.js:1791

Do you see anything I did wrong?

from angular-translate.

emiaj avatar emiaj commented on May 13, 2024

I stumbled upon this issue as well, I used a global hook that runs for each one of the tests:
https://gist.github.com/emiaj/7209915

With this I don't have to worry to remember on injecting the code to reset the translate provider on each fixture.
And if I need to create a test that involves asserting for translated content then I create a beforeEach that is under the scope of my fixture(describe(....)) and just override the default settings.

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

@emiaj Thanks for sharing your insights!

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

I'll provide some documentation in a later release about this topic. Closing this now.

from angular-translate.

zouberakis avatar zouberakis commented on May 13, 2024

Hi,

I am using your partial loader plugin and and have problems testing it. I was thinking about the way you're doing your gets.

angular-translate-loader-partial.js lines 14-17:

$http({
method: 'GET',
url: this.parseUrl(urlTemplate, lang)
})

Why don't you give this a try:

$http.get(this.parseUrl(urlTemplate, lang), {cache: $cacheFactory})

I assume the json files would be cached and we could simply test with $cacheFactory.put("apps_route_to_json_file", tests_route_to_json_file)

This could work. Right?

from angular-translate.

0x-r4bbit avatar 0x-r4bbit commented on May 13, 2024

Pinging @DWand here, thanks @x057 for the input!

from angular-translate.

DWand avatar DWand commented on May 13, 2024

@PascalPrecht, @x057, give me few days to finish my exams, please, and I'll be with you.

from angular-translate.

zouberakis avatar zouberakis commented on May 13, 2024

Hi again,

I've hacked the angular-translate-loader-partial.js myself and used $templateCache for it.

So the get should look like this:

$http.get(this.parseUrl(urlTemplate, lang), {cache: $templateCache})

And some lines below:

this.$get = ['$rootScope','$injector','$q','$http','$templateCache',function ($rootScope, $injector, $q, $http, $templateCache) {...})]

And of course line 10 should look like this:

Part.prototype.getTable = function (lang, $q, $http, urlTemplate, errorHandler, $templateCache) {...

Then in your tests before you start using .addPart or .uses add this:

template = $templateCache.get('app/i18n/main/en.json');
$templateCache.put('i18n/main/en.json', template);

It works perfect on my tests. Not 100% if templateCache is the ideal way to go though. But then again why not.

Don't forget to preprocess json files...I actually preprocessed the json files with ng-html2js. Go figure.

Anyway. Hope it helps.

from angular-translate.

DWand avatar DWand commented on May 13, 2024

Okay, @x057, @PascalPrecht, as for testing I'm not 100% sure if it's needed for tests because partial loader has a setPart method.

But we actually can add an extra option to partial loader to use standard angular caching. Again, I'm not sure if this is a good idea, This way we'll get duplicate of the same functionality because we are storing translations by ourselves. So, this way we have rewrite some additional logic of partial loader.

Possible problems are:

  1. Delete some part from the cache to refetch it from the server (now we just delete it physically)
  2. User can turn off caching at all and each time the loader will fetch parts from the server
  3. User can remove cached values from outside the loader
  4. User can call removeAll method and delete cached translations as a side effect of deleting some other cached values
  5. User can call destroy method and I don't know what will happen in this case

So, it's not so bad, but it could lead to some unpredictable behavior.

from angular-translate.

kaczmar2 avatar kaczmar2 commented on May 13, 2024

@emiaj When using the global hook (i18n.spec.base), I get [$compile:multidir] Multiple directives error: ex: error: [$compile:multidir] Multiple directives [myDirectiveA, myDirectiveB] asking for template on <div id...

Is there a way to prevent this from happening?

from angular-translate.

kaczmar2 avatar kaczmar2 commented on May 13, 2024

Issue was when setting up directive unit tests - this had to be done in a particular way. Otherwise, we would get the "multiple directives" error. You need to move the directive setup code before the mock data and call an additional $digest after the mock data setup:

// Wrong way
beforeEach(inject(function($rootScope, $compile)
{
  ... mock data ...
  confirmMessage = angular.element('<confirm-message/>');
  $compile(confirmMessage)(scope);
  scope.$digest();
}));

// Correct way: we need the following workaround
beforeEach(inject(function($rootScope, $compile)
{
  confirmMessage = angular.element('<confirm-message/>');
  $compile(confirmMessage)(scope);
  scope.$digest();
  ... mock data ...
  scope.$digest();
}));

from angular-translate.

aldopizzagalli avatar aldopizzagalli commented on May 13, 2024

thank you!!! You are saving my job and my time... ;)

from angular-translate.

ianmurrays avatar ianmurrays commented on May 13, 2024

I don't mean to reopen this issue, but I managed to "fix" my tests using this module:

'use strict';

angular.module('translateNoop', [])
  .factory('$translateStaticFilesLoader', function ($q) {
    return function () {
      var deferred = $q.defer();
      deferred.resolve({});
      return deferred.promise;
    };
  });

Just include this in karma and import the module after your app:

beforeEach(module('yourApp'));
beforeEach(module('translateNoop'));

Hope this helps. Previous solutions in this thread were not working for me and this seems to be simpler.

from angular-translate.

erezcohen avatar erezcohen commented on May 13, 2024

Sorry - not meaning to open this as well - however this could be useful:
We took the approach of ignoring the translation loader in unit tests, rather than being forced to modify each of the spec files.
See a simple solution at http://stackoverflow.com/a/29474122/145599.

from angular-translate.

ilkercat avatar ilkercat commented on May 13, 2024

@ianmurrays worked for me! ty

from angular-translate.

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.