Best practice for javascript test harness use

Hi, I’m looking for best practices with respect to using the test harness. I see that the popclip global isn’t available. I prefer to separate tests from the distributed artifacts but this seems like the cleanest way to handle that.

Is there a better way to approach testing? Using the Uppercase example for simplicity:

// # popclip
// name: Uppercase
// icon: square filled AB
// after: paste-result
// language: javascript

// Check if popclip exists. If not, use mock data.
var textToConvert = (typeof popclip !== 'undefined' && popclip.input) ? popclip.input.text : getMockData();

print(textToConvert.toUpperCase());
return textToConvert.toUpperCase();

// This function provides mock data for testing.
function getMockData() {
    // Return mock data or even read from some external source if needed.
    return "Mock input data here";
}

Thanks,
Cory

1 Like

A great question! First off, your approach seems a perfectly sound one.

I haven’t used the test harness much yet, either, so I’d say the best practices are yet to be formed.

When I have written tests for my extensions, I’ve experimented with a few approaches, and I haven’t settled on one. I do prefer to use module-based extensions, though. This way, I can export a function to be tested (or export a test function, depending on your preferred style) and then invoke that from a separate test module. Something like this:

// # popclip
// name: Uppercase
// icon: square filled AB
// after: paste-result
// language: javascript
// module: true

// the action itself, just a wrapper.
// this is what popclip will actually invoke.
function action() {
    return doTheThing(popclip.input.text)
}

// our implementation function to be tested.
function doTheThing(text) {
    return text.toUpperCase();
}

// a test function
function test() {
  const testData = [
    ['hello', 'HELLO']
    // ...
  ];
  // perform tests on doTheThing() function  
  for(const [testString, expectedResult] in testData) {
      // ...
  }
}

// it's safe to export `test` property -- it will simply be ignored by PopClip's extension loader
module.exports = { action, test }

Then in another file, test.js (or whatever), I’d require() the main extension file and run the tests – and it’s that file that I load in the test harness.

Alternatively, you could just invoke test() right there at the end of the main extension file. The downside is it’ll also be run every time the extension is first loaded, but as long as it runs quickly and has no side-effects, that’s not a problem.


More thoughts…
I think the harness could be improved a lot. Ideally, we could say to the test harness, "Load this file and then execute the exported function named “test”. Thus avoiding any need for a separate test loader file.
Another possibility is a more “end-to-end” style test harness with some way to run mock test data through the full extension.

P.S. if you get to anything you’re really happy with, I’d love to see the results.

2 Likes