Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JEST: Is there a solution to run tests only for changed/impacted files?

There are few solutions given by Jest in that direction. I have tried out most of the solutions like --findRelatedTests, --onlyChanged, --changedSince. But there are few shortcomings in every solutions. I thought --changedSince is the best match for me.

jest --changedSince=origin/master --coverage

It mostly covers the basic scenarios like running test files corresponding to the changed source files. But it does not handle few scenarios like if a source-file(say a.js) is deleted and same(a.js) is being used(imported) in another file(say b.js), it does not run tests for any of the files(a.js or b.js). It does not seem to run tests for parent files where it was imported.

Is there a clean solution which can handle all the scenarios like file rename/deletion, dynamic imports, running tests for the parent modules where it was imported or any other impact that may happen when you change a source file?

like image 886
Debajit Majumder Avatar asked Jan 28 '26 21:01

Debajit Majumder


1 Answers

Quick answer: No.

Long answer: Yes, but it's not that clean or straight forward.

I have achieved this through three steps.

STEP 1:

You can achieve this by a bit of scripting. First you'll want to get a list of all the changed files through Git.

This can be achieved through a function like the below:

const util = require("util")
const exec = util.promisify(require("child_process").exec)

const detectChangedFiles = async () => {
    try {
        const { stdout, stderr } = await exec("git diff origin/master --name-only")

        if (stderr) {
            throw new Error(stderr)
        }

        return stdout.replace(/\n/g, " ").replace(/client\//g, " ")

    } catch (error) {
        console.log(error)
    }
}

STEP 2:

Secondly, you'd wanna get a list of related tests for those files like the below:

 const findRelatedTests = async () => {
    const changedFiles = await detectChangedFiles()

    try {
        const { stdout, stderr } = await exec(`jest --listTests --findRelatedTests ${changedFiles}`)

        if (stderr) {
            throw new Error(stderr)
        }

        if (!stdout) {
            console.log('No tests found for the changed files :)')
        } else {
            return stdout.replace(/\n/g, " ")
        }

    } catch (error) {
        console.log(error)
    }
}

STEP 3:

And finally you'd wanna feed all of those tests to jest to run;

const runRelatedTests = async () => {
    const relatedTests = await findRelatedTests()

    if (relatedTests) {
        try {
            const { stdout, stderr } = await exec(`jest --ci --coverage ${relatedTests}`)

            if (stderr) {
                throw new Error(stderr)
            }
        } catch (error) {
            console.log(error)
        }
    }
}

One of the limitations of this implementation is that I"m always diffing against the master and that's not a good assumption. In special cases, one may chose to merge against another branch.

This can be handled in a few ways;

  1. If you're running a cli, pass the arguments to the cli and consume it in your script
  2. If you're running in pipeline like Gitlab and assuming that this is a MR/PR, consider using some available environment varibale ( in this case CI_MERGE_REQUEST_TARGET_BRANCH_NAME )
like image 134
Pouya Ataei Avatar answered Jan 30 '26 10:01

Pouya Ataei



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!