How to simulate Meteor clients?

I still advise you to use casper, it makes it much easier to write flows than with pure phantomjs. Learning curve is absolute low to just get some automated benchmark scripts. Just drop all “tests” funtions and use .wait(ramdom) and .then() to create some typical “user” behaviors.

overkill how? it simulates a user, right? are your programming problems so trivial that you don’t deserve a convenient library to solve them?

Hmm. How can I access the Meteor namespace?

I’m just tried PhantomJS to connect to my Meteor app:

console.log('Loading a web page');
var page = require('webpage').create();
var url = 'http://localhost:3000/';
page.open(url, function (status) {
  //Page is loaded!
  phantom.exit();
});

I’m getting all kinds of errors though. No errors in Chrome, Safari, or Firefox though…

❯❯❯ phantomjs client.js                                                                                                                                                   
Loading a web page
TypeError: 'undefined' is not a function (evaluating 'ReactElementValidator.createElement.bind')

  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:11008
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:7691 in createDOMFactory
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:20622 in mapObject
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:7835
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in s
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:4428
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in s
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:12231
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in s
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:3948
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in s
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:62
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in s
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41 in e
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:21610
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:41
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:21611
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:21614
  http://localhost:3000/packages/grove_react.js?ab44d041c618ba865114410f4bf28961e2709d92:21798
TypeError: 'undefined' is not an object (evaluating 'Package['grove:react'].ReactiveMixin')

  http://localhost:3000/packages/ccorcos_react-meteor.js?36c89113e3a515dfa53c8b47ed079de18aad784e:22
  http://localhost:3000/packages/ccorcos_react-meteor.js?36c89113e3a515dfa53c8b47ed079de18aad784e:305
TypeError: 'undefined' is not an object (evaluating 'Package['grove:react'].ReactiveMixin')

  http://localhost:3000/packages/global-imports.js?ae4f93ebbfbb8faf2a0e65d09fe6b894f10f17db:4
ReferenceError: Can't find variable: lodash

  http://localhost:3000/lib/00init00.coffee.js?5694fd2f5e551094a875265e7102969348295784:2
  http://localhost:3000/lib/00init00.coffee.js?5694fd2f5e551094a875265e7102969348295784:17
ReferenceError: Can't find variable: Comments

  http://localhost:3000/lib/collections.coffee.js?d8c5fea3489c4380e1d5bc2fb05bcfff567dc1d9:2
  http://localhost:3000/lib/collections.coffee.js?d8c5fea3489c4380e1d5bc2fb05bcfff567dc1d9:110
ReferenceError: Can't find variable: Meteor

  http://localhost:3000/lib/methods.coffee.js?fc406fa41047a474af52b6a5b8e399e2b36c7225:2
  http://localhost:3000/lib/methods.coffee.js?fc406fa41047a474af52b6a5b8e399e2b36c7225:10
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/components/Error.coffee.js?6337909738a1c76f295062ed25025cf72ba024bb:4
  http://localhost:3000/client/views/components/Error.coffee.js?6337909738a1c76f295062ed25025cf72ba024bb:16
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/components/EventItem.coffee.js?f7eeafd3542b3c81ccab9306be27ae69811ebf59:4
  http://localhost:3000/client/views/components/EventItem.coffee.js?f7eeafd3542b3c81ccab9306be27ae69811ebf59:84
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/components/Scroller.coffee.js?8b0104ffe109e5b17ad4226b2e3ddfd345bb5776:2
  http://localhost:3000/client/views/components/Scroller.coffee.js?8b0104ffe109e5b17ad4226b2e3ddfd345bb5776:73
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/components/Spinner.coffee.js?4a7150d07efcd4f04b3cac0d0c17ccb396d9f783:4
  http://localhost:3000/client/views/components/Spinner.coffee.js?4a7150d07efcd4f04b3cac0d0c17ccb396d9f783:26
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/layout/Body.coffee.js?ccaedf66fa20ac2e69a4fea7de46bf06552e2845:4
  http://localhost:3000/client/views/layout/Body.coffee.js?ccaedf66fa20ac2e69a4fea7de46bf06552e2845:16
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/layout/Header.coffee.js?40fe266eff3eeb52eaf22f19f9e843d1061c8573:4
  http://localhost:3000/client/views/layout/Header.coffee.js?40fe266eff3eeb52eaf22f19f9e843d1061c8573:38
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Explore.coffee.js?2990add72b5fbc8c1aad16fdcbf95448c9983213:4
  http://localhost:3000/client/views/Explore.coffee.js?2990add72b5fbc8c1aad16fdcbf95448c9983213:19
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Feed.coffee.js?59747a98def85f5c82ecf3591b061f5b798a0c3d:4
  http://localhost:3000/client/views/Feed.coffee.js?59747a98def85f5c82ecf3591b061f5b798a0c3d:45
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Plus.coffee.js?94338c2855fbbcb3397673abea0311dfc1c2fe25:4
  http://localhost:3000/client/views/Plus.coffee.js?94338c2855fbbcb3397673abea0311dfc1c2fe25:19
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Profile.coffee.js?7fd8ca9cae4b08ce434a1023fb2698abc6141738:4
  http://localhost:3000/client/views/Profile.coffee.js?7fd8ca9cae4b08ce434a1023fb2698abc6141738:19
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Search.coffee.js?f2ad6a5b444433cfbe2e04d48182e598c7e76a07:4
  http://localhost:3000/client/views/Search.coffee.js?f2ad6a5b444433cfbe2e04d48182e598c7e76a07:19
ReferenceError: Can't find variable: React

  http://localhost:3000/client/views/Welcome.coffee.js?8b67cc1c8db8c8d4db3f36e3ff9446fe8398b496:4
  http://localhost:3000/client/views/Welcome.coffee.js?8b67cc1c8db8c8d4db3f36e3ff9446fe8398b496:332
ReferenceError: Can't find variable: _

  http://localhost:3000/client/router.coffee.js?caad00d1dc4f6c749c7cd0056c45884dec70c440:26
  http://localhost:3000/client/router.coffee.js?caad00d1dc4f6c749c7cd0056c45884dec70c440:172

then drive a firefox or chrome in xvfb.

I’m just saying the simpler the solution, the better.

I will make you a short example

1 Like

That’s where you need Meteor Down: https://github.com/meteorhacks/meteor-down

4 Likes

All I want to do is set some random intervals for users logging in, creating subscriptions, calling methods, and logging out.

Here’s a simple idea for what I want to accomplish for one user.

newPost = ->
  Meteor.call 'newPost', faker.lorem.sentence()

subscriptions =  
  feed: ->
    Meteor.subscribe 'feed'
  search: ->
    Meteor.subscribe 'search', faker.characters(faker.random(1,10))
  detail: ->
    Meteor.subscribe 'post', _.sample(Posts.find().fetch())?._id

sub = null
randomSubscribe = ->
  sub?.stop?()
  sub = subscribe[_.sample(_.keys(subscriptions))]()

Meteor.loginWithPassword 'username', 'password', (error) ->
  Meteor.setInterval newPost, 5000
  Meteor.setInterval randomSubscribe, 2000

I suppose the benefit of Casperjs is so I can create all these subscriptions, and method calls etc. using the UI. That would be pretty sweet actually. @tomfreudenberg, what if I created some set of actions from each page and gave a probability of each action with different intervals – then I could just do a random walk around the webpage, clicking buttons, submitting forms etc. Maybe I can call the script with some number of clients, watch a bunch of printouts, and close all connected when I ctrl-c the process.

Here you are :slight_smile:

The original content is at my gist: https://gist.github.com/TomFreudenberg/994f107a029d152049eb

Hope you like it.


This is a very simple casperjs example, to show how to simulate a user accessing a meteor application like the todos example.

The casper script will do:

  1. connect to the todos example app
  2. add a new list
  3. iterate through all existing lists and open/load each once

Between each step there is a small random time, so that the “loads” are not aligned.

You can also enable casper debug to see a lot more debugging output that is very usefull for usability checks.

If you open your browser in parallel you will notice the growing number of the lists.

This was made and tested on MAC OSX with node@0.10.38, npm@1.4.28 and meteor@1.1.0.2.

First create a simple new example to use this demo script.

I expect, that the meteor server will run on default URL (localhost:3000)

# create a new meteor example

cd /tmp
meteor create --example todos
cd todos
meteor

Open an additional terminal to run the casper “user”

# install npms on first time
 
npm -g install casperjs
npm -g install phantomjs
npm -g install sugar

# get the casper script

cd /tmp
curl -L -O https://gist.githubusercontent.com/TomFreudenberg/994f107a029d152049eb/raw/48216827c9ab8e4170a404ed348930898ffe3afc/simple-actions-on-todos.casper.js

# run the magic

casperjs simple-actions-on-todos.casper.js

If you want to start a number of simulation processes in parallel, you can use bash technic like:

for ((i=1; i<=5; i++)) do casperjs simple-actions-on-todos.casper.js & done


Remark: I have added a number of requires that are not necessary on this casper script but I advise you to use them, cause those will help on more complex scripts.


  1. Casperjs module documentation
  2. Casperjs hompage
  3. Phantomjs homepage
  4. Sugarjs Features
2 Likes

Just updated my previous post.

This looks great! I’m definitely going to use this.

Is is possible to create a script that doesn’t end until I stop it with ctrl-c? Perhaps I can just call a recursive function within casper.then?

I’d also like to handle multiple users logging in and out randomly/dynamically in JS. Rather than use bash is it possible to do this in Javascript? Perhaps calling casper.start more than once?

If I may give you the advise, I would not create too complex scripts. We have good experiences also for testing and quality checks, when creating a number of seperated scripts - each with a defined usecase.

If you at the end use a bash script (as we do) or write some simple javascript childprocess umbrella, that depends on your personal preference. But with such an umbrella starter (or also a number of sets) you are much more flexible to produce random load on your app.

This is like we do.

I see. Thanks for the help! I’ll let you know how it goes…

So are you saying not to let a client script run indefinitely?

I think it is not possible but you can add a surrounding eachThen loop and use great sugarjs function :smile:

casper.eachThen((1).upto(100), function(response){

  // your complete suite loop

});

You have to recognize, that inside each level of inner functions() the this object pointer is your friend.

casper.then(function(){
  this.then(function(){
    this.then(function(){
      ...

with that you can create stepwise procedures with ease

Do NOT use casper. in inner functions. Only the outest should use the casper object.

IMHO infinitely should be a NO-GO

It would be nice to see things changing and working as I’m building…

Just run:

while [[ 1 ]]; do casperjs your-script.sugar.js; done

That is infinitely

CasperJS might do the job, but I just wanted to throw out a warning against Selenium and Nightwatch for anyone who might try using those for stress (aka load) testing. I got it to work for a while, but when I recently tried to re-run my stress tests they failed for reasons I could not determine.

And I had to summon a lot of horsepower to stress test with Selenium & Nightwatch. Great tools for functional/end-to-end/web tests, but not so much stress tests.

+1 for Meteor Down. That’s a tool meant for stress testing. You need to be able to quantify your Meteor app workload in terms like pub/sub performance, observer reuse, and method response time. Functional tests are brittle. UI changes break them, timeouts don’t always work. PhantomJS is a yet another browser you’ll have to support. Your app’s API (Meteor methods, publications) is less likely to break in mysterious ways. The interface is more conducive to scripting (as expected).

You’ll miss out on bugs and performance problems resulting from complex client+server interactions (reactivity causing excessive DDP traffic, oversubscription), so just keep that in mind. Kadira usually reveals these types of issues.

Here’s a thread about testing your Meteor app using PhantomJS with your own AWS EC2 instances to test with distributed, headless browser instances that will actually download and test your app using a WebDriver test case. It makes use of a good cloud load tester called www.redline13.com:

https://forums.meteor.com/t/poor-galaxy-meteor-performance-serving-small-bursts-of-users-load-test/38671