unsafe_where_true
unsafe_where_true

Reputation: 6320

angular: write a service which mocks my real backend

I need to write code which accesses data in a backend.

Trouble is that backend is not ready yet. So I want to inject a service which would, possibly by configuration, use the real backend or a mock object.

I think angular services are what I need. But I am not clear how to implement this.

app.factory('myService', function() {
  var mySrv;

  //if backend is live, get the data from there with $http
  //else get data from a mock json object
  return mySrv;
});

Somehow I guess I'd have to write two more services, the real one and the fake one, and then call the one or the other in 'myService'?

Maybe I totally misunderstand mocking, but I'd rather not want this to be mocked for test runs (not for unit tests like in this post: Injecting a mock into an AngularJS service - I'd like the app to really use my mock for demo and development purposes.

Upvotes: 1

Views: 177

Answers (2)

cconolly
cconolly

Reputation: 366

Does this need to be a service? If you want to create a mock service then Josh's answer above is perfect.

If your not tied to using a service then I suggest looking at my answer to the following question Mock backend for Angular / Gulp app which also about mocking out a backend. Regardless of wether your backend is created or not mocking it out allows for more stable test runs and development of you app.

Upvotes: 0

Josh
Josh

Reputation: 44916

This is actually where Angular's dependency injection system really comes in handy. Everything in Angular is basically a provider at some level. Methods like service and factory are just convenience functions to avoid boilerplate code.

A provider can be configured during the bootstrap process, which is really handy for setting up scenarios exactly like what you are describing.

At it's simplest, a provider just needs to be a constructor function that creates an object with a $get function. The $get is what creates the actual services, and this is where you can check to see which one to create.

//A simple provider
function DataServiceProvider(){

  var _this = this;

  _this.$get = function(){
    //Use configured value to decide which
    // service to return
    return _this.useMock ? 
             new MockService() : 
             new RealService;
  };

}

Now you can register this as a provider with your application module.

angular.module('my-module', [])
  .provider('dataService', DataServiceProvider);

And the provider can be configured before it creates the first service instance. By convention, the provider will be available as NAME + 'Provider'

angular.module('my-module')
  .config(['dataServiceProvider', function(dataServiceProvider){
    //Set the flag to use mock service
    dataServiceProvider.useMock = true;
  }]);

Now whenever you inject dataService anywhere in your application, it will be using the mock service you provided based on configuration.

You can see a full working example of this in the snippet below.

(function(){

  function RealService(){
    this.description = 'I\'m the real McCoy!';
  }
  
  function MockService(){
    this.description = 'I\'m a shady imposter :P';
  }
  
  function DataServiceProvider(){
    var $this = this;
    
    $this.useMock = false;
    
    $this.$get = function(){
      return $this.useMock ?
          new MockService() :
          new RealService();
    };
  }
  
  function CommonController(dataService){
    this.dataServiceDescription = dataService.description;
  }
  CommonController.$inject = ['dataService'];
  
  angular.module('common', [])
    .provider('dataService', DataServiceProvider)
    .controller('commonCtrl', CommonController);
  
  angular.module('provider-app-real-service', ['common'])
    .config(['dataServiceProvider', function(dataServiceProvider){
      dataServiceProvider.useMock = false;
    }]);
  
  angular.module('provider-app-mock-service', ['common'])
    .config(['dataServiceProvider', function(dataServiceProvider){
      dataServiceProvider.useMock = true;
    }]);
  
  
  var moduleNames = ['provider-app-real-service','provider-app-mock-service'];
    
  angular.forEach(moduleNames, function(modName){
    //Have to manually bootstrap because Angular only does one by default
    angular.bootstrap(document.getElementById(modName),[modName]);
  }); 
  
}());
<script src="http://code.angularjs.org/1.3.0/angular.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />

<div class="container">
  <div class="row" id="provider-app-real-service">
    <div class="col-sm-12" ng-controller="commonCtrl as ctrl">
      <h1>{{ctrl.dataServiceDescription}}</h1>
    </div>
  </div>
  <div class="row" id="provider-app-mock-service">
    <div class="col-sm-12" ng-controller="commonCtrl as ctrl">
      <h1>{{ctrl.dataServiceDescription}}</h1>
    </div>
  </div>
</div>

Upvotes: 1

Related Questions