7

I have a javascript autocomplete plugin that uses the following classes (written in coffeescript): Query, Suggestion, SuggestionCollection, and Autocomplete. Each of these classes has an associated spec written in Jasmine.

The plugin is defined within a module, e.g.:

(function(){
  // plugin...
}).call(this);

This prevents the classes from polluting the global namespace, but also hides them from any tests (specs with jasmine, or unit-tests with something like q-unit).

What is the best way to expose javascript classes or objects for testing without polluting the global namespace?

I'll answer with the solution I came up with, but I'm hoping that there is something more standard.

Update: My Attempted Solution

Because I'm a newb with < 100 xp, I can't answer my own question for 8 hours. Instead of waiting I'll just add what I did here.

In order to spec these classes, I invented a global object called _test that I exposed all the classes within for testing. For example, in coffeescript:

class Query
  // ...

class Suggestion
  // ...

// Use the classes

// Expose the classes for testing
window._test = {
  Query: Query
  Suggestion: Suggestion
}

Inside my specs, then, I can reveal the class I'm testing:

Query = window._test.Query

describe 'Query', ->
  // ...

This has the advantage that only the _test object is polluted, and it is unlikely it will collide with another definition of this object. It is still not as clean as I would like it, though. I'm hoping someone will provide a better solution.

8
  • Maybe expose some internal variable, only? Example: window.exposedVars = this (inner of closure)? Commented Dec 20, 2011 at 1:20
  • I just added my proposed solution a few seconds after you posted this. Is it along the lines of what you were suggesting? Commented Dec 20, 2011 at 1:30
  • 1
    I think this is a similar question to "How do I test private methods in Java". You don't. You test only the public interface of the module in question, i.e. whatever it exports or otherwise exposes. If you have a massive piece of internal implementation that you think needs to be tested separately, you may consider making it a module of its own. Using an import mechanism like "require", it does not need to pollute anything except the module hash managed by the module manager. Commented Dec 20, 2011 at 2:26
  • 1
    @Thilo I agree that testing the public interface and event behaviour is most important. It sure is nice having tests or specs of your internal behaviour though. A good spec on internal classes would make it lightyears easier to refactor when I've long forgotten why I've implemented things a certain way. I feel that that's worth having to do things a little non-standardly to expose functionality to tests. Plus, I wanted to TDD this thing from the top down. Commented Dec 20, 2011 at 17:48
  • 1
    I'm curious if anyone knows an example of a jQuery plugin with really good spec coverage. I certainly wish there were more out there, based on some really bad experiences adapting untested plugins. Fingers crossed for the jQuery community adopting higher expectations of its plugins! Commented Dec 20, 2011 at 17:57

1 Answer 1

3

I think something like the CommonJS module system (as used by brunch, for example) would work.

You can separate your code into modules, and the parts that need them would import them via require. The only part that gets "polluted" is the module map maintained by the module management code, very similar to your test object.

In Autocomplete.coffee

class exports.Query
// ...

class exports.Suggestion
// ...

and then in Autocomplete.spec.coffee

{Query, Suggestion} = require 'app/models/Autocomplete'

describe 'Query', ->
Sign up to request clarification or add additional context in comments.

1 Comment

Interesting. I hadn't looked in to this system before. I suppose it's quite similar in essence to what I did. For a light-weight plugin you want to share with others though, I'm not sure I'd want to throw in a whole module management system. Still, the brunch page is a good, relevant read.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.