Monday, April 27, 2015

4/27/15 - Sinon Deep Equals Problems

In Javascript, {} == {} and {} === {} both evaluate to false. This is because objects in javascript must be compared via deep equals, which iterates through all the keys in the object and checks their equality recursively. So expect({}).equal({}) (regular equal) would cause a test failure, expect({}).eql({}) (deep equal) would pass the test.

In sinon, we've been using the method mock.expects("method").once().withExactArgs(ARG_OBJECT), which compares an object passed into a method with the object we expect it to be (in this case, an object called ARG_OBJECT), which worked here. Now that I think about this, I'm not actually sure why this works. When Finn tried the same thing for our BookDownloadManager tests, the #withExactArgs would fail even though the object being passed in to the expect and the object being recieved by the mock were exactly the same. There is no way (that I can see, at least) to get an exact args deep equals, so we're basically out of luck. Tomorrow we'll try to look into more solutions.

Actually, I looked into it further, and it turns out I jumped to conclusions a bit too fast. I was right to wonder why mock.expects("method").once().withExactArgs(ARG_OBJECT); works when the code we were working with today doesn't, because as it turns out, deep equals is not the problem. As far as I can tell, #withExactArgs does use deep equals.

The problem was actually more deceptive: We use mock.verify() at the end of each test to show that the methods are being called correctly. I assumed that .verify() would clear the expectations so that each test would start fresh, but that doesn't seem to be the case. Instead, our expectations from the first test kept carrying over to the next, causing failure. (Although as I say that, I'm looking here, where we did have multiple .verify()s that worked in succession, so I might be wrong again).

To fix this problem, I'm recreating the mock object after each test, which will start fresh with no expectations:
beforeEach(function () {
    storageMock = sinon.mock(storage); // must re-mock for each test
});
I have no idea if this is what you're supposed to do, because none of the examples I could find used multiple verify statements, but it does work. Luckily, creating the mock isn't a very processor intensive operation, and we don't have many tests (yet), so this doesn't cause any noticeable performance decrease.

Here's the test at this point in time. I also moved the helper objects into their own file, which is automatically loaded with tests.js thanks to this line in karma.conf.js. Splitting tests.js up into smaller test files will just be a cut and paste operation!

No comments:

Post a Comment