Jest Unit Test check for unique value

Viewed 750

I am new to unit tests and writing them for my components and class. I have a method which filters duplicate numbers but with the same ID. I wrote this method:

public groupByOrderNumber(
    deliveryReceipts: DeliveryReceiptSearch[]
): Map<string, Map<string, DeliveryReceiptItemSearchResult[]>> {
    const groupedDeliveryReceipts = new Map<string, Map<string, DeliveryReceiptItemSearchResult[]>>();

    for (const receipt of deliveryReceipts) {
        const receiptId: string = receipt.id;
        groupedDeliveryReceipts.set(receiptId, new Map<string, DeliveryReceiptItemSearchResult[]>());
        for (const item of receipt.items) {
            const orderNumber = item.orderNumber;
            if (groupedDeliveryReceipts.get(receiptId).has(orderNumber)) {
                groupedDeliveryReceipts.get(receiptId).get(orderNumber).push(item);
            } else {
                groupedDeliveryReceipts.get(receiptId).set(orderNumber, [item]);
            }
        }
    }

    return groupedDeliveryReceipts;
}

My tests are like this:

describe('groupByOrderNumber', () => {
    const deliveryReceipts: DeliveryReceiptSearchMock[] = DeliveryReceiptSearchMock.createListOfReceipts();

    it('should not contain identical orderNumbers', () => {
        const dReceipts: Map<string, Map<string, DeliveryReceiptItemSearchResult[]>> = service.groupByOrderNumber(
            deliveryReceipts
        );

        for (const receipts of deliveryReceipts) {
            const receiptId: string = receipts.id;
            for (const item of receipts.items) {
                const orderNumber = item.orderNumber;
                expect(dReceipts.get(receiptId).has(orderNumber)).toIn
          expect(dReceipts.get(receipts.id).set(orderNumber, [item])).not.toEqual(dReceipts.get(receipts.id).get(orderNumber));
            }
        }
    });
});

The test will mock some data for the purpose of the test as seen at the beginning. I am not quite sure how I test if the orderNumber is unique to its' receipt.id.

Is there a way to use some native jest methods?

Example:

receipt.id ('9876') -> orderNumber ('123')
receipt.id ('101112') -> orderNumber ('123')

That's what groupByOrderNumber() basically does.

2 Answers

You could just create a nested dictionary and immediately fail if you see a duplicated identifier on the nested dictionary checking the order number.

In this manner we avoid looping more than once over the entire collection.

function testReceipts(deliveryReceipts) {
  const orderCheck = {}
  for (const receipt of deliveryReceipts) {
    orderCheck[receipt.id] = Boolean(orderCheck[receipt.id]) ? orderCheck[receipt.id] : {};
    for (const item of receipt.items) {
      const orderNumber = item.orderNumber;
      expect(!orderCheck[receipt.id].hasOwnProperty(orderNumber)).toBeTruthy();
      orderCheck[receipt.id][orderNumber] = ""
    }
  }
}

just for the heck of it I wrote a small stub to run the testReceipts test

// arrange
const knownGood = [{
    id: 1,
    items: [{
      orderNumber: 1
    }, {
      orderNumber: 2
    }, {
      orderNumber: 3
    }]
  },
  {
    id: 2,
    items: [{
      orderNumber: 1
    }, {
      orderNumber: 2
    }, {
      orderNumber: 3
    }]
  }
];

const knownBad = [{
    id: 1,
    items: [{
      orderNumber: 1
    }, {
      orderNumber: 2
    }, {
      orderNumber: 1
    }]
  },
  {
    id: 2,
    items: [{
      orderNumber: 1
    }, {
      orderNumber: 2
    }, {
      orderNumber: 3
    }]
  }
];

// assert
testReceipts(knownGood); // pass.
testReceipts(knownBad); // Bad code, bad!

// I didn't want to install Jest, so I made my own little expect wrapper. You'd obviously use Jest.
function expect(arg) {
  return ({
    toBeTruthy: () => {
      if (!Boolean(arg)) console.error("Bad code, bad!");
    }
  })
}


First, you need to create a map that stores the receipts by its id, so you can access all the receipts with the same id very easy.

Then, for each receipt id, get the order number.

And last, check that those order number are unique (convert the array to a Set and compare their length).

const deliveryReceipts = [{
  id: '1',
  orderNumber: '123'
}, {
  id: '1',
  orderNumber: '234'
}, {
  id: '2',
  orderNumber: '123'
}, {
  id: '3',
  orderNumber: '456'
}, {
  id: '3',
  orderNumber: '456'
}];
const receiptsMap: {
  [id: string]: DeliveryReceiptSearchMock[]
} = {};

// This iterates over every receipt and stores them in a map based on their ID.
for (const receipt of deliveryReceipts) {
  const receiptId: string = receipt.id;
  receiptsMap[receiptId] = [...receiptsMap[receiptId] || [], receipt];
}

// For each receiptId, let's test if the orderNumbers are unique.
for (const receiptId of Object.keys(receiptsMap)) {
  const allOrderNumbers = receiptsMap[receiptId].map((receipt) => receipt.orderNumber);
  const uniqueOrderNumbers = [...new Set(allOrderNumbers)];
  // expect(allOrderNumbers.length).toBe(uniqueOrderNumbers.length)
  expectToEqual(receiptId, allOrderNumbers.length, uniqueOrderNumbers.length);
}

function expectToEqual < T > (receiptId: string, given: T, expected: T): void {
  if (given === expected) {
    console.log(`id ${receiptId} ✓`);
  } else {
    console.log(`id ${receiptId} ✗ Expected ${expected} but found ${given}`);
  }
}

Related