Reputation: 13262
I've having a strange problem with this test:
import Deal from "../src/models/Deal";
import apiProducts from "../__mocks__/api/products";
describe("Deal", () => {
describe("Deal.fromApi", () => {
it("takes an api product and returns a Deal", () => {
const apiDeal = apiProducts[0];
const newDeal = Deal.fromApi(apiDeal);
const expected = expectedDeal();
expect(newDeal).toEqual(expected);
});
});
});
export default class Deal {
// no constructor since we only ever create a deal from Deal.fromApi
static fromApi(obj: Object): Deal {
const deal = new Deal();
deal.id = obj.id;
deal.name = obj.name;
deal.slug = obj.slug;
deal.permalink = obj.permalink;
deal.dateCreated = obj.date_created;
deal.dateModified = obj.date_modified;
deal.status = obj.status;
deal.featured = obj.featured;
deal.catalogVisibility = obj.catalog_visibility;
deal.descriptionHTML = obj.description;
deal.shortDescriptionHTML = obj.short_description;
deal.price = Number(obj.price);
deal.regularPrice = Number(obj.regular_price);
deal.salePrice = Number(obj.sale_price);
deal.dateOnSaleFrom = obj.date_on_sale_from;
deal.dateOnSaleTo = obj.date_on_sale_to;
deal.onSale = obj.on_sale;
deal.purchasable = obj.purchasable;
deal.relatedIds = obj.related_ids;
deal.upsellIds = obj.upsell_ids;
deal.crossSellIds = obj.cross_sell_ids;
deal.categories = obj.categories;
deal.tags = obj.tags;
deal.images = obj.images;
return deal;
}
descriptionWithTextSize(size: number): string {
return this.descriptionWithStyle(`font-size:${size}`);
}
descriptionWithStyle(style: string): string {
return `<div style="${style}">${this.description}</div>`;
}
distanceFromLocation = (
location: Location,
unit: unitOfDistance = "mi"
): number => {
return distanceBetween(this.location, location);
};
distanceFrom = (otherDeal: Deal, unit: unitOfDistance = "mi"): number => {
return distanceBetween(this.location, otherDeal.location);
};
static toApi(deal: Deal): Object {
return { ...deal };
}
}
The test fails with this error:
● Deal › Deal.fromApi › takes an api product and returns a Deal
expect(received).toEqual(expected) // deep equality
Expected: {"catalogVisibility": "visible", "categories": [{"id": 15, "name": "New York", "slug": "new-york"}], "crossSellIds": [34, 31], "dateCreated": "2019-05-18T17:36:14", "dateModified": "2019-05-18T17:39:02", "dateOnSaleFrom": null, "dateOnSaleTo": null, "descriptionHTML": "<p>Pete's Tavern<br />
129 E 18th St<br />
New York, NY 10003</p>
<p>Weekdays from 4 p.m. to 7 p.m.<br />
$5 wines and beers</p>
", "distanceFromLocation": [Function anonymous], "featured": false, "id": 566, "images": [{"alt": "", "date_created": "2019-05-18T17:38:52", "date_created_gmt": "2019-05-18T17:38:52", "date_modified": "2019-05-18T17:38:52", "date_modified_gmt": "2019-05-18T17:38:52", "id": 567, "name": "wine and beers2", "src": "https://tragodeals.com/wp-content/uploads/2019/05/wine-and-beers2.jpg"}], "name": "Wines and beers", "onSale": true, "permalink": "https://tragodeals.com/product/wines-and-beers/", "price": 5, "purchasable": true, "regularPrice": 11, "relatedIds": [552, 564, 390, 37, 543], "salePrice": 5, "shortDescriptionHTML": "<p>$5 wines and beers</p>
", "slug": "wines-and-beers", "status": "publish", "tags": [{"id": 58, "name": "beers", "slug": "beers"}, {"id": 54, "name": "Cocktails", "slug": "cocktails"}, {"id": 45, "name": "drink", "slug": "drink"}, {"id": 57, "name": "wine", "slug": "wine"}], "upsellIds": [53]}
Received: serializes to the same string
> 15 | expect(newDeal).toEqual(expected);
| ^
16 | });
17 | });
18 | });
at Object.toEqual (__tests__/deal.test.js:15:23)
I inserted this loop to investigate:
for (let key in expected) {
expect(expected[key]).toEqual(newDeal[key]);
}
And I see that the problem is with functions. So I changed the whole test to this:
for (let key in expected) {
if (typeof expected[key] === "function") continue;
expect(expected[key]).toEqual(newDeal[key]);
}
// expect(newDeal).toEqual(expected);
And it passes, and also fails when it should. (if you read the old version of this question where I was getting passing tests that I didn't understand, it was because I was return
ing from the loop when I should have been continue
ing).
But I'd like to be able to do it with the standard assertion expect(newDeal).toEqual(expected)
. It looks like there's something I'm not understanding about checking for class object (Deal
) equality with functions.
PS. You might suggest using toMatchObject
. But, sadly:
● Deal › Deal.fromApi › takes an api product and returns a Deal
expect(received).toMatchObject(expected)
- Expected
+ Received
@@ -1,6 +1,6 @@
- Deal {
+ Object {
"address": "129 E 18th St New York, NY 10003",
"catalogVisibility": "visible",
"categories": Array [
Object {
"id": 15,
13 | expect(expected[key]).toEqual(newDeal[key]);
14 | }
> 15 | expect(newDeal).toMatchObject(expected);
| ^
16 | });
17 | });
18 | });
Upvotes: 204
Views: 209024
Reputation: 155
In my case, I was instantiating new objects in two different comparisons instead of using the same object, so they weren't "equal" but they did serialize the same:
Failing:
const givenMap = new Map([
['foo', new APIImageSource('foo/bar')]
]);
const expectedMap = new Map([
['foo', new APIImageSource('foo/bar')]
]);
expect(ResourceFetcher.getAllResources().size).toBe(0);
TestObject.testMethod(givenMap);
/* This was failing because expectMap was creating a new APIImageSource */
expect(ResourceFetcher.fetchByKeys(['foo', 'new'])?.values()).toMatchObject(expectedMap.values());
Working
const givenMap = new Map([
['foo', new APIImageSource('foo/bar')]
]);
/* I am now consuming the reference to the object already created, so it is now "equal" (it's the same object) */
const expectedMap = new Map([
['foo', givenMap.get('foo')]
]);
TestObject.testMethod(givenMap);
expect(ResourceFetcher.fetchByKeys(['foo']).values()).toEqual(expectedMap.values());
By using the reference to the object already created in the "given map" I can still compare against the "expected map"
For simplicity, I removed the extra properties I was testing against.
Upvotes: 0
Reputation: 5358
In my case it was because of a function included inside the object. So it should be checked this way:
expect(obj).toEqual({ foo: expect.any(Function), bar: true })
Upvotes: 1
Reputation: 153
I got this error when wanted to assert to log object.
Solved this issue with create a new context
describe('assert deep', () => {
testScheduler = new TestScheduler((actual, expected) => {
expect(JSON.stringify(actual)).toEqual(JSON.stringify(expected));
});
// What suite you want with this configuration
})
Upvotes: 0
Reputation: 71
The output for mongoose.Types.ObjectId is a complex type - converting it via .toHexString()
allows you to compare it to other objectId (and it also makes the objectid readable to jest).
//from this:
expect(res.body).toHaveProperty('genre._id', genrePayload._id);
//to this:
expect(res.body).toHaveProperty('genre._id', genrePayload._id.toHexString());
Upvotes: 0
Reputation: 79
I had a similar issue while comparing two MongoDb ObjectIds. To overcome the problem, I used
expect(`${thisObject._id}`).toEqual(`${thatObject._id}`);
Upvotes: 7
Reputation: 91
I had this same issue with jest. In my case I was comparing the array of objects (basically a model class). And in that class I had defined a function as an arrow function.
The problem is, while comparing it checks for the arrow functions also. And as arrow functions create different instances for all the objects in contrast to normal function which have only one instance class-wide, the arrow function comparison results false.
So a simple solution would be to convert your arrow functions to normal functions in classes.
Use:
class ClassName {
yourFunc(){
// your code here
}
};
Instead of:
class ClassName {
yourFunc = () => {
// your code here
};
};
So once converted to normal function you can simply use toEqual()
for comparison.
If you can't convert to normal function you can use JSON.stringify()
to convert them first to strings and then use toEqual()
or toBe()
.
Upvotes: 6
Reputation: 615
I had this problem when i tried to compare arrays where one array was coming back from the Graphql's resolver and the other one was from my test's input.
ALL the fields were the same except the entries inside the array coming from Graphql did not have any __proto__
while the ones from my test input had __proto__: Object
and that cause the toStrictEqual to fail, because it checks the types besides the structure. So, in my case the type caused to fail. Changing it to toEqual
solved the problem.
expect(res.data.myMutation.values).toEqual(
testInput.values,
);
Upvotes: 17
Reputation: 7545
In my situation, I was deep equal checking a proxied object vs a regular object.
const proxyObject = new Proxy({}, {
get: function(target, prop) {
return Object.hasOwnProperty.call(target, prop) ? target[prop] : '';
}
})
expect(proxyObject).toEqual({})
// Expected: {}
// Received: serializes to the same string
It should be:
expect(proxyObject).toMatchObject({})
Upvotes: 14
Reputation: 6587
I had a similar case where the object had a base64 encoded string, I managed the test to compare the serialization of the object using JSON.stringify
:
expect(JSON.stringify(newDeal)).toMatchObject(JSON.stringify(expected));
Upvotes: 58
Reputation: 3367
Similarly to other colleagues I had this issue with an Array comparison, I was basically testing a function that got the largest string in an array, additionally it should return an array if more than 1 of those strings matched the largest length possible.
When I started testing I got the following message:
So I replaced the toBe
method
expect(function(array1)).toBe('one result')
with toStrictEqual
to make a deep equality comparison
expect(function(array2)).toStrictEqual(['more than one', 'more than one']);
Upvotes: 262
Reputation: 6213
Just had this problem when tried to compare arrays where in one array there was an element with -1 index set (imagine any other key to be set except numbers from 0 to N). So you may have this error in the following scenario:
const arr = [1, 2]
arr[-1] = 'foo'
expect(arr).toEqual([1, 2])
They both serialized to the same string, but they are not equal.
Upvotes: 41