> ## Content Index
> Fetch the complete content index at: https://www.workroom-productions.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Exercise: Custom Asserts
- URL: https://www.workroom-productions.com/custom-asserts-exercise/
- Published: 2025-10-29T08:51:30.000Z
- Updated: 2025-11-06T23:44:33.000Z
- Description: Play with building custom asserts
- Author: James Lyndsay
- Tags: Exercises

*For these exercises, we'll use Github Codespaces. You will need a github account. You will need to run this on your own account, which has ?60 hours a month free codespaces.*

In this exercise. we'll use LLMs to help us build custom asserts. You'll use your own access to an LLM, and we'll work in environments with a JavaScript test harness, a Python test harness, and code which could do with a custom assert or two. 

We'll look at the tiny systems, identify what would help, ask LLMs to build working asserts, see what they give back, and compare notes.

Here's more on [custom asserts](https://www.workroom-productions.com/custom-asserts/).

## Environment

I've built something that has all the code, test harnesses and plumbing for you to try this in JavaScript and Python. You'll access your own copy of that environment via VisualStudio Code in your browser.

## Exercise 0 – your own environment

Go to the [Custom Asserts repo](https://github.com/workroomprds/WorkroomPlayTime036%5FCustomAsserts) , and you should see a `readme` page – click on the "open in Github Codespaces" button, wait and respond reasonably to Github's questions.

Further instructions are in the readme – once you're in, you'll need to run something on the commandline to install all the infrastructure dependencies, and more to run tests. You can browse the software under test while the dependencies are being installed.

The environment has two exercises, each in both Javascript and in Python. I'm *still*  building the examples, so if you set up early, you won't have them.

### Infrastructure

The tests for my JavaScript examples here use [Chai](https://www.chaijs.com/) (assertion library) and [Mocha](https://mochajs.org/) (unit test framework). My Python examples use bare Python and [pyTest](https://pytest.org/) (unit test framework). 

You'll need to go to your own LLM to ask for code – you'll find examples below.

#### Use your own LLM?

You don't **have* an LLM – but you may have access to one. If not, you may use the 'free' version of [ChatGPT](https://chatgpt.com) or use a burnable email for [Claude](https://claude.ai/), or you can use copilot in the sidebar of the browser-based VSCode window.

#### Python and assertion libraries

Assertion libraries seem not as prevalent in the Python community as [Chai](https://github.com/chaijs/chai) and others are in the JS community. 

Perhaps that is because it is simple and common to build custom asserts directly in Python. Pytest leans heavily on Python’s own assert and has a hook `pytest_assertrepr_compare` for detailed messages.

Python does have assertion libraries – [pyHamcrest](https://github.com/hamcrest/PyHamcrest) being the standout example.

## Pick a codebase, get the tests running

Different tech has different tests. I've tried to make this exercise work in Python and in Javascript / Node. Both are readable: you might want to work in your less-familiar technology. Or you might want to compare.

You'll find Python in `./python_project` and JavaScript / Node in `./javascript_project`. 

I've tried to give the files and functions similar names. 

## Exercise 1 – get the tests running 

Get the tests running. 

Have a look at the `calculator` code and tests to see how they work.

## Exercise 2 – checking XML tags

We'll build a custom assert to check XML tags.

#### Context – `MT103` \-> `PACS.008`

An `MT103` is a Swift transaction that acts as a proof of payment. It has recently been superseded by `PACS.008` which is XML-based. As an intermediary step in preparation for the change, some organisations reformatted the old record into XML, passing that through their live systems. Shared test asserts to do specific XML verification are useful when coding that change. 

- `MT103_gen.js` and `MT103_gen.py` have a function `generateMT103` which returns a simple XML example record.
- `./test/test_MT103_gen.js` and `./tests/test_MT103_gen.py` both test that function.

We're going to use this as a dummy to build a custom assert. We want the custom assert to check that all the XML tags are present, and that there are no extras. 

For the sake of plumbing build the custom assert in a file called `customAsserts.js`/`.py`, and call the method `containXmlTags`. 

You'll need to register it before using it. Using the names above, you could

```javascript
// Register the custom assertion
const containXmlTags = require('./customAsserts');
chai.use(containXmlTags);

```

```python3
from customAsserts import containXmlTags
```

And, of course, you'll need to add the test...

Once you've added it, you can test it by changing the generation code `MT103_gen`.

We'll compare and contrast our experiences.

### Examples of generated test code

*My approach – for greatest transparency, I'm copying the code and chucking it straight into Claude 4.*

> Given the following code, and the following tests, make me a shareable custom assert called `containXMLtags` in a file `customAsserts.js`. The custom assert should check that all tags for this example MT103 are present, and none are extra.  
> \[I pasted the code in here\]

*which returned:*

Sure, here's the `customAsserts.js` file with the `containXMLtags` custom assert:

```javascript
const { DOMParser } = require('@xmldom/xmldom');

const containXMLtags = (actual, expected) => {
  const parser = new DOMParser();
  const doc = parser.parseFromString(actual, 'text/xml');

  // Check for parse errors
  const parseError = doc.getElementsByTagName('parsererror');
  if (parseError.length > 0) {
    return {
      message: () => 'The provided XML string is not valid.',
      pass: false,
    };
  }

  // Check root element exists
  if (!doc.documentElement) {
    return {
      message: () => 'The provided XML string does not have a root element.',
      pass: false,
    };
  }

  // Check for expected tags
  const expectedTags = [
    'MessageType',
    'SenderBIC',
    'ReceiverBIC',
    'TransactionReference',
    'ValueDate',
    'Amount',
    'Currency',
    'BeneficiaryName',
    'BeneficiaryAccount',
    'BeneficiaryAddress',
    'RemittanceInformation',
  ];

  for (const tag of expectedTags) {
    if (!doc.getElementsByTagName(tag).length) {
      return {
        message: () => `The expected tag '${tag}' is not present in the XML.`,
        pass: false,
      };
    }
  }

  // Check for extra tags
  const extraTags = Array.from(doc.getElementsByTagName('*'))
    .map((node) => node.nodeName)
    .filter((tag) => !expectedTags.includes(tag));

  if (extraTags.length > 0) {
    return {
      message: () => `The following unexpected tags were found in the XML: ${extraTags.join(', ')}`,
      pass: false,
    };
  }

  return {
    message: () => 'The provided XML string contains all expected tags and no extra tags.',
    pass: true,
  };
};

module.exports = { containXMLtags };

```

To use this custom assert in your tests, you can import it from the `customAsserts.js` file:

```javascript
const { expect } = require('chai');
const { containXMLtags } = require('./customAsserts');

describe('MT103_gen', () => {
  let generateMT103;

  before(() => {
    try {
      ({ generateMT103 } = require('../MT103_gen'));
    } catch (err) {
      generateMT103 = null;
    }
  });

  it('should generate valid XML', () => {
    const result = generateMT103();
    expect(result).to.satisfy(containXMLtags);
  });

  it('XML should be labelled a SwiftPayment record', () => {
    const result = generateMT103();
    expect(result).to.satisfy(containXMLtags);
  });
});

```

This does both more (more checks) and less (it's not generic) than we'd asked for. I might re-prompt, asking for the test to contain the list of tags.

*Note: these are examples – you'll get different stuff, and it might be better. I've not yet used* this *example.*

## Exercise 2 – checking a distribution

We'll build a custom assert to check that a certain type of record is in the 'right' proportion to other records.

#### Context: a simulation

In the simulation, items need to be common, rare, and very rare. There is lots of code to make the items; we need a check for all the different options to let us know if some items are too rare, or not as rare as they should be.

- `candidate_gen` generates candidates
- `test_candidate_gen` does very little

We're going to use this as a dummy to build a custom assert. We want the custom assert to check that 8-12% of the candidates are of type `high`, 0.5 to 1.5 % are `mighty` and the rest (and no more than 91%) are `base`.

Again, you'll need to register your custom assert, and use it in a test.

For the sake of plumbing build the custom assert in a file called `customAsserts.js`/`.py`, and call the method `checkDistribution`. 

Adjust the candidate generation to test it – note that the dummy *should* fail. It has too few records to have a `mighty`. So you'll need to make different distributions to properly test the assert. Tested *test* code? Whatever next!? 

### Examples of generated test code

> I want to build a custom assert to check candidate proportions. The assert should take parameters. In this case, 8-12% of the candidates should be of type high, 0.5 to 1.5 % are mighty and the rest (and no more than 91%) are base. Example candidate generation and existing test code follows. The custom assert should be called \`checkDistribution\` and will be used in a file called `customAsserts.py`

*we'll build examples in the workshop.*