Question

I have a web application with an Angular / Breeze client side calling into a Breeze Web API, which uses an Entity Framework code first model. I have a datacontext (Angular service) responsible for all communications with server.

I would like to completely separate the server development from the client side development so developers need not even have .NET installed on their system. I would like the solution to require very little coding in way of creating fakes, because the app is changing frequently and I do not want to have to rewrite fakes every time my implementation changes. I have a bunch of test data in the database that I would like to make available on the client.

What is a good way (standard way?) to achieve this?

Was it helpful?

Solution 2

Here is my current solution.

  1. Get data from the server with a 'unit test' that creates a Breeze Web API controller and uses it to gather the breeze metadata and all the test data from the database, then writes that data to testData.json and breezeMetadata.json.

  2. Abstract the creation of the Breeze Entity Manager to an Angular service entityManager.

  3. Create a fakeEntityManager Angular service, which: 1) creates the entity manager, 2) overrides the EntityManager.executeQuery function to always use the local version, and 3) loads up the mgr with the test data. The code for that service is below.

  4. In the datacontext service, use the $injector service to conditionally inject a real or a fake entity manager.

datacontext.js

angular.module('app').factory('datacontext', ['$injector','config', datacontext]);

function datacontext($injector, config) {

    if (config.useLocalData === true) {
        var mgr = $injector.get('fakeEntityManager');
    } else var mgr = $injector.get('entityManager');

    ...

fakeEntityManager.js

(function() {
    'use strict';

    var serviceId = 'fakeEntityManager';
    angular.module('app').factory(serviceId, ['breeze', 'common', em]);

    function em(breeze, common) {
        var $q = common.$q;
        var mgr = getMgr();
        populateManager(["Projects", "People", "Organizations"]);
        return mgr;            

        function getMgr() {
            breeze.EntityManager.prototype.executeQuery = function(query) {
                return $q.when(this.executeQueryLocally(query)).then(function (results) {
                    var data = {
                        results: results
                    };
                    if (query.inlineCountEnabled == true) data.inlineCount = results.length;
                    return data;
                });
            };
            var metaData = < PASTE JSON HERE > 
            new breeze.ValidationOptions({ validateOnAttach: false }).setAsDefault();
            var metadataStore = new breeze.MetadataStore();
            metadataStore.importMetadata(metaData, true);

            return new breeze.EntityManager({
                dataService: new breeze.DataService(
                    {
                        serviceName: "fakeApi",
                        hasServerMetadata: false // don't ask the server for metadata
                    }),
                metadataStore: metadataStore
            });
        }

        function populateManager(resources) {
            var testData = < PASTE JSON HERE >;
            resources.forEach(function (resource) {
                testData[resource].forEach(function (entity) {
                    mgr.createEntity(mgr.metadataStore.getEntityTypeNameForResourceName(resource), entity);
                });
            });
        }
    }
})();

If you don't use inlineCount queries there is no need to override executeQuery. You can just add the following property to the EntityManager constructor's parameter:

queryOptions: new breeze.QueryOptions({ fetchStrategy: breeze.FetchStrategy.FromLocalCache })

Todo: Override the EntityManager.saveChanges() function (or somehow configure the entity manager) to prevent calls to the server while still allowing entities to be edited and saved locally.

OTHER TIPS

Just create mocks. You don't even have to make a RESTful call if you don't want to, just have your service decide whether to hit the server or pull from cache and load up your cache locally on start -

function loadMocks (manager) {
    var personMockOne = manager.createEntity('Person', { id: 1, firstName: 'John', lastName: 'Smith' });
    var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' });
    companyMockOne.employees.push(personMockOne);
}

http://pwkad.wordpress.com/2014/02/02/creating-mocks-with-breeze-js/

To Expand...

Doing this requires a bit of extra set up. I personally always write my queries separate from my controller / view model logic through a service which takes parameters. A few example parameters are always something like parameters and forceRemote. The idea is that when you go to execute the query you can decide whether to hit the server or query locally. A quick example -

function queryHereOrThere (manager, parameters, forceRemote) {
    var query = breeze.EntityQuery().from('EntityName').using(manager);
    query.where(parameters);

    if (!forceRemote) {
        query.executeQueryLocally();
    } else {
        query.executeQuery();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top