The $get() function is required; it is called to retrieve the SomeProvider service _after_ config() has been called. When using the provider as a provider, if you have a provider named “Foo”, you will inject “FooProvider”, which is automatically created for you. It’s like two services in one! Note that you cannot inject SomeProviderProvider into anything after bootstrapping has finished. This means you must only use your provider (“FooProvider”) in the config() function, before the services have initialized.

A real-world example of a useful provider would be a generic “dialog” or “pop-up” provider (“Dialog” or “DialogProvider”), which you could configure during the config() stage to use any number of different implementations. For example, you may want to tell “DialogProvider” to use jQuery UI, or Bootstrap, or some hand-rolled thing. That way, each module or application using the provider (living in some common module) will be able to declare what implementation to use, and the service (“Dialog”) will act accordingly. You should probably even allow this service to be configurable on-the-fly if, for some reason, you need to use a Bootstrap modal here and a jQuery UI dialog there (ew).

Unit Testing Services

Unit testing factories, services and providers is incredibly easy. Let’s write tests for our Posts service and our Post factory:

 describe('Blog Services', function () {
    var Posts, httpBackend;

    // I want to use the variable name Posts for my service, but if I use DI here to inject it
    // the variable name will already be taken! A fun way around this limitation isto inject the $injector service,
    // then simply ask it for what you want.
    beforeEach(inject(function ($injector, $httpBackend) {
        Posts = $injector.get('Posts');
        httpBackend = $httpBackend;
    }));

    describe('Posts', function () {
        it('should initiate the http call', function () {
            var promise;
            // you will need to use expectGET, expectPOST, etc., when unit testing functions
            // that use the $http service. They are assertions about what call you are making and allow
            // you to mock a response easily.
            httpBackend.expectGET('data.json').respond('foo');
            promise = Posts.getPosts();
            // make sure our promise is really a then-able object
            expect(angular.isFunction(promise.then)).toBeTruthy();
            promise.then(function (res) {
               expect(res.data).toBe('foo');
            });
            httpBackend.flush();
        });
    });
});

Next is a relatively long bit of testing code to assert all of our Post class’ methods are functioning as expected:

 describe('Blog Factories', function () {

    var Post;

    beforeEach(inject(function ($injector) {
        Post = $injector.get('Post');
    }));

    describe('Post', function () {

        // covers all of the constructor's behavior, which is very little.
        describe('the constructor', function () {

            it('should store information temporarily when autosave flag is not truthy', function () {
                var post = new Post('cashews', 'peanuts', new Date(), 'Mr. Almond');
                expect(post.temp.title).toBe('cashews');
                expect(post.temp.body).toBe('peanuts');
                expect(post.title).toBeUndefined();
                expect(post.body).toBeUndefined();
                expect(post.date).not.toBeUndefined();
                expect(post.author).toBe('Mr. Almond');
            });

            it('should not store information temporarily when autosave flag is truthy', function () {
                var post = new Post('cashews', 'peanuts', new Date(), 'Mr. Almond', true);
                expect(post.title).toBe('cashews');
                expect(post.body).toBe('peanuts');
                expect(post.temp.title).toBeUndefined();
                expect(post.temp.body).toBeUndefined();
                expect(post.date).not.toBeUndefined();
                expect(post.author).toBe('Mr. Almond');
            });
        });

        describe('the updateDate method', function () {
            it('should reset the date', function () {
                var old_date, post = new Post();
                old_date = post.toString();
                post.updateDate();
                // it would be kind of weird/difficult to try to assert it updates the date object
                // to NOW; so just assert it changed.
                expect(old_date).not.toBe(post.date.toString());
            });
        });

        describe('the save method', function () {
            it('should copy information from temp object', function () {
                var post = new Post('foo', 'bar');
                post.save();
                expect(post.title).toBe('foo');
                expect(post.body).toBe('bar');
            });

            it('should call updateDate method', function() {
               var post = new Post();
               // we just need to assert that save() happens to call the updateDate() method
               spyOn(post, 'updateDate');
               post.save();
               expect(post.updateDate).toHaveBeenCalled(); 
        });

            it('should call the stopEditing method', function() {
                var post = new Post();
                spyOn(post, 'stopEditing');
                post.save();
                expect(post.stopEditing).toHaveBeenCalled();
            });
        });

        describe('the beginEditing method', function() {
            it('should rearrange some data in the post', function() {
                var post = new Post('foo', 'bar', new Date(), 'baz', true);
post.beginEditing();
                // we assert the state changes and the information has been copied out of temp
                expect(post.editing).toBeTruthy();
                expect(post.temp.title).toBe(post.title);
                expect(post.temp.body).toBe(post.body);
            });
        });

        describe('the stopEditing method', function() {
            it('should set editing flag to false and empty temp object', function() {
                var post = new Post('foo', 'bar', new Date(), 'baz', true);
                post.beginEditing();
                post.stopEditing();
                expect(post.editing).toBeFalsy();
                // note that you cannot use "toBe" here, because {} !== {}
                expect(post.temp).toEqual({});
            });
        });

        describe('toString method', function() {
            it('should return a JSON string', function() {
                var post = new Post('foo', 'bar', new Date(), 'baz', true);
                expect(post.toString()).toBe(angular.toJson(post));
            });
        });
    });
});

Conclusion

These services, providers and factories make up the backbone (no pun intended) of your angularjs app. Do the heavy lifting here; make it reusable and testable. You’ll end up with less spaghetti on your plate. Keep your controller from thinking too hard. Your directive only has one function in it–make that function readable by injecting services into it.