How do I use CoffeeScript to mock out existing code?

549 Views Asked by At

I'd like mock out MarkdownDeep, I've the following code, in JavaScript

MarkdownDeep = new (function () {
    this.Markdown = function () {
        this.Transform = function (a) {
            return "html";
        };
    };
})();

but I'm having trouble implementing this in CoffeeScript

I tried the following

MarkdownDeep = new (->
  @Markdown = ->
    @Transform = (a) ->
      "html"
)()
window.MarkdownDeep = MarkdownDeep

but it doesn't work, specifically in my unit test markdown = new MarkdownDeep.Markdown() gives "undefined is not a function", though the JS version mocks out fine.

5

There are 5 best solutions below

1
On BEST ANSWER

Your example results in the following javascript code:

var MarkdownDeep;
MarkdownDeep = new (function() {
  return this.Markdown = function() {
    return this.Transform = function(a) {
      return "html";
    };
  };
});
window.MarkdownDeep = MarkdownDeep;

The line return this.Markdown = function() { /* ... */ } makes the function the object returned by the new operator.

Writing

MarkdownDeep = new (->
  @Markdown = ->
    @Transform = (a) ->
      "html"
    return
  return
)
window.MarkdownDeep = MarkdownDeep

fixes the problem.

Addition: This answer mentions the algorithm for object construction in javascript

2
On

CoffeeScript automatically wraps each output file in an anonymous function ((function() { ... })()). To disable this use the --bare or -b option when running coffee.

0
On

You need to explicitly set a return value for your objects/classes or else it will return the member functions when creating a new instance.

JS FIDDLE

MarkdownDeep = new (->
  @Markdown = ->
    @Transform = (a) ->
      "html"
    undefined #return undefined instead of this.Transform
  undefined #return undefined instead of this.Markdown
)

markdown = new MarkdownDeep.Markdown()
alert markdown.Transform()

compiles to:

var MarkdownDeep, markdown;
MarkdownDeep = new (function() {
  this.Markdown = function() {
    this.Transform = function(a) {
      return "html";
    };
    return;
  };
  return;
});
markdown = new MarkdownDeep.Markdown();
alert(markdown.Transform());
0
On

CoffeeScript's implicit returns can lead to mayhem when used in conjunction with new. As others have pointed out, you could use explicit returns. Another option is to use class, which creates a function (the constructor) with no implicit return:

MarkdownDeep = new class
  constructor: ->
    @Markdown = class
      constructor: ->
        @Transform = (a) ->
          'html'

Of course, that's not very readable in this case, but as a general rule, you'll save yourself headaches by using class whenever you use new.

0
On

This is what Coffeescript gives as output

var MarkdownDeep;
MarkdownDeep = new (function() {
  return this.Markdown = function() {
    return this.Transform = function(a) {
      return "html";
    };
  };
});

The last line in every function is implicitly returned in Coffeescript. Checking this in the console yields MarkdownDeep as

function () {
    return this.Transform = function(a) {
        return "html";
    };
}

which returns a function that does not have Markdown() as a method.