Unit testing a model that uses a transform, which uses a service (need to mock a service in a unit test)


#1

Hi everyone,

I’ve added a transform to a model of mine to deal with numbers with decimals (the problem of comma vs dot vs other separator). I want the backend to always receive numbers with dots, so I’ve create a transform called “decimal-value” that does that. It checks the current user’s decimal separator and replaces it with a dot.

export default DS.Transform.extend({
userSession: service(),
  serialize(deserialized) {
   //do stuff and check the user session
  }
})

I’ve changed my models to use this transform in some of the attributes,

heatTemperature: attr('decimal-value'),

But now the unit test for this particular model fails with:

Promise rejected before it converts to json: Attempting to register an unknown factory: `session:user-session`

I’ve added the required needs on the model unit test to include the transform:decimal-value

Should I mock the user-session service? How do I do that in a unit test? Should I be following some different strategy?

Thanks for any input.


#2

There is a way to do this, but I am thinking that it might be better to keep a value in the database with no dots or formatting ( generic ) , and then format the view you show.

So I would save in the database the generic thing ( number with no dots ), show the formatted thing in the input box that they can edit. Then when they edit, reformat and save the generic thing with no formatiing, if you know what I mean? ?


#3

Hi Daniel, thanks for replying :slight_smile:

I see what you mean. That’s mostly what I’m trying to do (numbers are always stored in the same format in the database), and in the front-end since I need to make calculations with them and Javascript only supports decimals with a dot as separator, internally in the front-end they are always converted to a dot separated decimal.

When I need the user to edit or to display the number to the user, I get the separator from the user-preferences and format it accordingly. This transform that I added was more of a fail-safe mechanism (in case I didn’t properly convert to the correct format) it allows me to at the last moment (before sending to the server) to convert to the correct format.


#4

Can you do man a favor and put the test code here so I can see what you are doing. Hard to help with no context.


#5

Sure thing,

decimal-value/transform.js:

import DS from 'ember-data';
import Ember from 'ember';

const { inject: { service }, isEmpty } = Ember;

export default DS.Transform.extend({
 userSession: service(),

  deserialize(serialized) {
    // There's no need to deserialize as the backend will always send decimals with dot a never with a comma
    return serialized;
  },

  serialize(deserialized) {
    // This shouldn't have to happen as the app should deal with this, but just in case
    // add this second layer of protection
    let separator = this.get('userSession.decimalSeparator');
    if (separator === ',') {
      if (!isEmpty(deserialized)) {
        let noComma = `${deserialized}`.replace(separator, '.');
        return parseFloat(noComma);
      }
    } else {
      if (!isEmpty(deserialized)) {
        return parseFloat(deserialized);
      }
    }
    return deserialized;
  }
});

Model:

 export default DS.Model.extend(

    testTemperature: attr('decimal-value'),

Test:

import { moduleForModel } from 'ember-qunit';

moduleForModel('welding-map', 'Unit | Model | welding map', {
  needs: ['transform:decimal-value']
});

// Tests cannot be executed due to dependency on service by transform:decimal-value
// skip('it exists', function(assert) {
//    let model = this.subject({});
//    assert.ok(!!model);
//  });

And pretty much that’s it (i’ve removed somethings just because of size)


#6

instead of needs you could do integration:true, ( you could also do service:session ) but inttegration:true always works

and then in the setup/beforeEach let session = this.container.lookup(‘service:session’); session.set(‘decimalIdentifier’, ‘,’)

and you are good to go


#7

Hey Daniel, thanks again for your patience and help, much appreciated.

I never though of transforming the unit test into an integration test, but it works just as you said. It does make sense, being an integration test that more things are loaded and as such it won’t break as the unit test did. Very interesting.

Thanks a lot for the help Daniel, best regards.