Re: [jasmine-js] Jasmine Clock setTimeout implementation failure

316 views
Skip to first unread message

Rajan Agaskar

unread,
Nov 1, 2012, 10:16:09 AM11/1/12
to jasmi...@googlegroups.com
It would be useful to see the RedisGetAction code here, as I think I'd
expect the failure -- were the mock clock working incorrectly -- to be
that you *never* go to the ready state (if waiting is the default),
rather than getting to ready right away.

I'm assuming RedisGetAction calls get on the client stub on
construction, right?

The other offhand things I could think of:

1) you're ticking the clock in some other before that is applied to
this test and jasmine may decide that you've already ticked past 1500,
so runs your code immediately
2) you're using some other library that interacts poorly with the mock
clock (I have heard of some mocking add-ons that seem to have issues
here).

Thanks!

Rajan

On Wed, Oct 31, 2012 at 11:02 PM, Brandon Wilburn
<techie....@yahoo.com> wrote:
> I hate to repost across multiple forums but I haven't get any response from
> Stackoverflow so thought it worth a shot here.
>
> http://stackoverflow.com/questions/13167741/jasmine-coffeescript-clock-not-effective
>
> I am getting no joy from my jasmine.Clock. My expectation is that the code
> will mock out the clock object and will trigger setTimeout events upon
> setting the tick past the specified intervals in the setTimeout, however
> this does not seem to be working and I cannot find my flaw. My code seems to
> be parallel to others applying the same clock control behavior.
>
> Background: the 'callback' function sets the this.action.state() to
> Constants.state.Ready after execution, prior it should be
> Constants.state.WAITING. Please note I am using knockout observables; the
> state is supposed to be called as a fx to retrieve value.
>
> describe "Redis GET Action", () ->
> beforeEach () ->
> jasmine.Clock.useMock();
> this.getReturnValue = getReturnValue = "Some jasmine values from redis"
> clientStub =
> get: (key,callback) ->
> if callback?
> setTimeout(callback(undefined, getReturnValue),1500)
> GET: (key,callback) ->
> if callback?
> setTimeout(callback(undefined, getReturnValue),1500)
>
> this.action = new RedisGetAction(
> client: clientStub
> key: "Standard"
> )
>
>
> it "should return a object", () ->
> expect(this.action).toEqual(jasmine.any(Object))
>
> requiredMethods = ['state','data','params'];
>
> requiredMethods.forEach (methodName) ->
> it "should have public "+methodName+" method", () ->
> expect(this.action[methodName]).toBeDefined();
>
> it "should initialize with public accessible state of
> #{Constants.state.WAITING}", () ->
> expect(this.action.state()).toEqual(Constants.state.WAITING)
> jasmine.Clock.tick(1501);
> expect(this.action.state()).toEqual(Constants.state.READY)
>
> Results: Failures:
>
> 1) should initialize with public accessible state of waiting
> Message:
> Expected 'ready' to equal 'waiting'.
> Stacktrace:
> Error: Expected 'ready' to equal 'waiting'.
>
> --
> You received this message because you are subscribed to the Google Groups
> "Jasmine" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/jasmine-js/-/KvIHndF2l34J.
> To post to this group, send email to jasmi...@googlegroups.com.
> To unsubscribe from this group, send email to
> jasmine-js+...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/jasmine-js?hl=en.

Brandon Wilburn

unread,
Nov 1, 2012, 10:25:10 AM11/1/12
to jasmi...@googlegroups.com
Rajan,

Your assumption is correct about the RedisGetAction class. Below you will find my latest and greatest attempts here with the console output. Thanks for the response!


describe "Redis Actions", () ->
  describe "GET", () ->
    beforeEach ->
      jasmine.Clock.useMock()
      # jasmine.Clock.reset()
      @getReturnValue = getReturnValue = "Some jasmine values from redis"
      @clientStub = 
        get: (key,callback) =>
          if callback?
            # console.log(jasmine.Clock.isInstalled())
            callFx = () ->
              console.log "get callback..."
              callback(undefined, getReturnValue)
            console.log "Using FakeClock... [#{jasmine.Clock.isInstalled()}]"
            setTimeout(callFx,1500)
        GET: (key,callback) =>
          if callback?
            callFx = () ->
              console.log "GET callback..."
              callback(undefined, getReturnValue)
            console.log "Using FakeClock... [#{jasmine.Clock.isInstalled()}]"
            setTimeout(callFx,1500)

      @action = new TND.redis.actions.RedisGetAction(
        client: @clientStub
        data: uid: "JasmineTestUID"
        )
      spyOn(@clientStub, 'get');
      spyOn(@clientStub, 'GET');
      

    it "should return a object", () ->
      expect(@action).toEqual(jasmine.any(Object))

    requiredMethods = ['state','data','params'];

    requiredMethods.forEach (methodName) ->
      it "should have public "+methodName+" method", () ->
        expect(@action[methodName]).toBeDefined();

    it "should call client's GET action", () ->
      expect(@clientStub.get).toHaveBeenCalled();

    it "should initialize with public accessible state of #{Constants.state.WAITING}", () ->
      
      console.log "Clock is fake?... [#{jasmine.Clock.isInstalled()}]"
      console.log "should be before callback... [#{@action.state()}]"
      expect(@action.state()).toEqual(Constants.state.WAITING)
      jasmine.Clock.tick(2500);
      console.log "should be after callback... [#{@action.state()}]"
      expect(@action.state()).toEqual(Constants.state.READY)
    
    it "should update public accessible data upon async return", () ->
      expectedValue = @getReturnValue
      # console.log expectedValue
      waitUntil = () ->
        @action.state() is Constants.state.READY
      waitsFor waitUntil, "public accessible data never gets populated", 20000

      runs () ->
        # jasmine.Clock.tick(2500)
        # console.log @action.data()
        expect(@action.data()).toEqual(expectedValue)

---------------------------------------------

  class RedisGetAction

    constructor : (cfgIn) ->
        state = ko.observable Constants.state.WAITING
        data = ko.observable({})
        params = ko.observable({})
        _cfg =
          client: undefined
          data : 
            uid : undefined

        _handleGetResponse = (err,dataIn) =>
          # console.log arguments
          if err? 
            console.log err
          data(dataIn)
          state(Constants.state.READY)

        _requestValue = () =>
          _cfg.client.get _cfg.data?.uid, _handleGetResponse
        _activate = () =>
          _requestValue()

        _updateConfig = (cfgIn) =>
          $.extend true, _cfg, cfgIn

        _updateConfig cfgIn
        _activate() 

        return {
          state: state
          data: data
          params: params
          uid : new String(_cfg.data.uid)
        }

---------------------------------------------

Using FakeClock... [true]
Using FakeClock... [true]
Using FakeClock... [true]
Using FakeClock... [true]
Using FakeClock... [true]
Using FakeClock... [true]
Clock is fake?... [true]
should be before callback... [waiting]
should be after callback... [waiting]
Using FakeClock... [true]
get callback...
get callback...
get callback...
get callback...
get callback...
get callback...
get callback...

Redis Actions

  GET
    should return a object
    should have public state method
    should have public data method
    should have public params method
    should call client's GET action
    should initialize with public accessible state of waiting
    should update public accessible data upon async return

Failures:

  1) should call client's GET action
   Message:
     Expected spy get to have been called.
   Stacktrace:
     Error: Expected spy get to have been called.
    at new jasmine.ExpectationResult (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:102:32)
    at null.toHaveBeenCalled (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1171:29)
    at null.<anonymous> (/Users/brandonwilburn/devTree/06-25-12/spui/demo/dcIntegration/src/spec/units/tndRedisGetAction.spec.coffee:54:44)
    at jasmine.Block.execute (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1001:15)
    at jasmine.Queue.next_ (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1790:31)
    at jasmine.Queue.start (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1743:8)
    at jasmine.Spec.execute (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:2070:14)
    at jasmine.Queue.next_ (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1790:31)
    at onComplete (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1786:18)
    at jasmine.Spec.finish (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:2044:5)

  2) should initialize with public accessible state of waiting
   Message:
     Expected 'waiting' to equal 'ready'.
   Stacktrace:
     Error: Expected 'waiting' to equal 'ready'.
    at new jasmine.ExpectationResult (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:102:32)
    at null.toEqual (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1171:29)
    at null.<anonymous> (/Users/brandonwilburn/devTree/06-25-12/spui/demo/dcIntegration/src/spec/units/tndRedisGetAction.spec.coffee:62:44)
    at jasmine.Block.execute (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1001:15)
    at jasmine.Queue.next_ (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1790:31)
    at jasmine.Queue.start (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1743:8)
    at jasmine.Spec.execute (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:2070:14)
    at jasmine.Queue.next_ (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1790:31)
    at onComplete (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:1786:18)
    at jasmine.Spec.finish (/usr/local/lib/node_modules/jasmine-node/lib/jasmine-node/jasmine-2.0.0.rc1.js:2044:5)

Finished in 1.524 seconds
7 tests, 8 assertions, 2 failures

Brandon Wilburn

unread,
Nov 1, 2012, 10:33:30 AM11/1/12
to jasmi...@googlegroups.com
As a response to your other lines of thought:
  - I am working against jasmine-node (downloaded from npm yesterday)
  - This test/suite is being ran in isolation (you have full suite on prior msg)
  - below are my other libraries in play:

  "dependencies": {
    "express": "3.0.0rc4",
    "coffee-script": "1.3.3",
    "redis": "0.8.1",
    "hiredis": "0.1.14",
    "socket.io": "0.9.10",
    "moment": "1.7.2",
    "xml2js": "0.2.0",
    "jquery": "1.7.3",
    "knockout": "2.1.0",
    "underscore": "1.4.2",
    "jade": "0.27.6",
    "consolidate": "0.4.0",
    "underscore.string": "2.3.0",
    "nodetime": "0.4.7",
    "jasmine-node": "1.0.26",
    "should": "1.2.0"

Rajan Agaskar

unread,
Nov 1, 2012, 1:38:34 PM11/1/12
to jasmi...@googlegroups.com
On my really brief reading, I'm wondering if your _cfg ref doesn't get
correctly extended?

_updateConfig = function(cfgIn) {
return $.extend(true, _cfg, cfgIn);
};

The way I'm reading this (post-conversion to js), is that it's
creating a new object (true) and extending that with _cfg (default
cfg) and cfgln (passed cfg), not updating your original _cfg ref.

Thus your _cfg ref in requestValue wouldn't get updated with the
passed client which would explain why the stub client isn't called.

That said, I would also expect the requestValue method to explode
because cfg.client would be undef'd.

I think the easiest approach is to just throw a debugger in your
requestValue method and poke at the _cfg vals.

Thanks!

Rajan
> --
> You received this message because you are subscribed to the Google Groups
> "Jasmine" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/jasmine-js/-/txfnvQJI7f4J.

Brandon Wilburn

unread,
Nov 1, 2012, 3:01:34 PM11/1/12
to jasmi...@googlegroups.com
Rajan, 

Again, thanks for taking the time with me! 

Some Jquery documentation:
http://api.jquery.com/jQuery.extend/

jQuery.extend( [deep], target, object1 [, objectN] )


I am using this pattern of updating the internal _cfg object across a entire library so I am very confident in its behavior. The _updateConfig does indeed get the client and the uid from cfgIn and overlays (deeply) ontop of the default _cfg. If we look at the last bit of my post we can see the execution order of the callbacks (which couldn't happen if requestValue was borking) in addition to some general information about the global timer/clock in each scope. To me this feels like a scoping issue (likely around how I have setup the jasmine.Clock and the clientStub get method) but I have been unsuccessful in locating the problem point. As a sidebar, the RedisGetAction class functions correctly when using the real client, I am just trying to wrap in some tests to give me confidence going forward when I hand off to other developers.

Regards,

Brandon Wilburn

unread,
Nov 1, 2012, 4:23:57 PM11/1/12
to jasmi...@googlegroups.com
So a bit more debugging and I realized I have some interesting interactions between the spyOn and the calling of the new RedisGetAction fx in addition to the clock in use inside the anonFx wasn't the FakeTimer. Thanks for the additional ideas!

Solution for all greens:

    beforeEach ->
      jasmine.Clock.useMock()
      @getReturnValue = getReturnValue = "Some jasmine values from redis"
      @clientStub = 
        get: (key,callback) =>
          if callback?
            # console.log(jasmine.Clock.isInstalled())
            callFx = () =>
              console.log "get callback... [start]"
              callback(undefined, getReturnValue)
              console.log "Using FakeClock... [#{jasmine.Clock.isInstalled()}]"
            jasmine.Clock.defaultFakeTimer.setTimeout(callFx,500)
        GET: (key,callback) =>
          if callback?
            callFx = () ->
              console.log "GET callback... [end]"
              callback(undefined, getReturnValue)
              console.log "Using FakeClock... [#{jasmine.Clock.isInstalled()}]"
            jasmine.Clock.defaultFakeTimer.setTimeout(callFx,500)

      spyOn(@clientStub, 'get').andCallThrough();

      @action = new TND.redis.actions.RedisGetAction(
        client: @clientStub
        data: uid: "JasmineTestUID"
        )
    afterEach ->
      jasmine.Clock.reset()

------------------------------------------------------


    it "should update public accessible state to '#{Constants.state.READY}' when data value is returned", () ->
      jasmine.Clock.tick(501);
      console.log "should be after callback... [#{@action.state()}]"
      expect(@action.state()).toEqual(Constants.state.READY)
    
    it "should update public accessible data upon async return", () ->
      expectedValue = @getReturnValue
      # console.log expectedValue
      waitUntil = () ->
        @action.state() is Constants.state.READY
      jasmine.Clock.tick(501)
      waitsFor waitUntil, "public accessible data never gets populated", 1000

      runs () ->
        # console.log @action.data()
        expect(@action.data()).toEqual(expectedValue)
Reply all
Reply to author
Forward
0 new messages