-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error when testing parent and child components where child invokes an apex method #232
Comments
This is an interesting issue! Thanks for sharing your findings and the great repro steps @filiprafalowicz; they helped a lot. As you mention, with v0.12.3, we started "auto-mocking" apex methods (at the resolver level), but before, they also were auto-mocked at the transform level (to In the repro, jest.mock(
'@salesforce/apex/MyApexCtrl.myApexAction',
() => {
return {
default: jest.fn()
};
},
{ virtual: true }
); If Looping @trevor-bliss and @pmdartus (the experts in jest) in case I missed something in my analysis. |
Hey @jodarove - thanks for your detailed response. When I removed my mock for the module both tests are passing now, which is great. One thing that comes to my mind regarding this is to include that in migration doc. I haven't seen it mentioned anywhere that this mocking is no longer needed. So all would be good and resolved, but... I think something is still wrong with those auto-mocks. If you change cmpB to make two Apex calls instead of just one, then those auto-mocks are messed up and results are not correct. It is no longer related to the parent - child component issue, but if you run tests for cmpB only, you will see the errors. Here are new snippets for cmpB: import { LightningElement, api } from 'lwc';
import myApexAction from '@salesforce/apex/MyApexCtrl.myApexAction';
import myAnotherApexAction from '@salesforce/apex/MyApexCtrl.myAnotherApexAction';
export default class CmpB extends LightningElement {
@api
async callApex() {
const myApexActionResult = await myApexAction();
const myAnotherApexActionResult = await myAnotherApexAction();
return {
myApexActionResult,
myAnotherApexActionResult,
};
}
} // cmpB.test.js
import { createElement } from 'lwc';
import myApexAction from '@salesforce/apex/MyApexCtrl.myApexAction';
import myAnotherApexAction from '@salesforce/apex/MyApexCtrl.myAnotherApexAction';
import CmpB from 'c/cmpB';
describe('c-cmp-b', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
it('should call both myApexAction and myAnotherApexAction when callApex called', async () => {
myApexAction.mockResolvedValue();
myAnotherApexAction.mockResolvedValue();
const element = createElement('c-cmp-b', {
is: CmpB
});
document.body.appendChild(element);
await element.callApex();
// First assertion fails with 2 calls received
expect(myApexAction).toHaveBeenCalledTimes(1);
expect(myAnotherApexAction).toHaveBeenCalledTimes(1);
});
it('should return correct result when callApex called', async () => {
myApexAction.mockResolvedValue('myApexActionValue');
myAnotherApexAction.mockResolvedValue('myAnotherApexActionValue');
const element = createElement('c-cmp-b', {
is: CmpB
});
document.body.appendChild(element);
const result = await element.callApex();
// Received object is: { myApexActionResult: "myAnotherApexActionValue", myAnotherApexActionResult: "myAnotherApexActionValue" }
expect(result).toEqual({
myApexActionResult: 'myApexActionValue',
myAnotherApexActionResult: 'myAnotherApexActionValue',
});
});
}); So in this case, it seems that only one Apex mock is working and it is sending the same value for both Apex calls. I tested it with the fix from #231, but it didn't change anything - result is still wrong. On the other hand, if I keep my own, virtual mocks, all of the tests are passing, but if I keep it, I will face the issue I reported originally. |
mmm, great point. This use case wasn't analyzed when doing the auto mocking at the resolver level; I remember that @pmdartus had some pushbacks, but (IMHO) this is a show stopper unless we figure out a way to make @filiprafalowicz, let me discuss with the team, ill get back to you soon. |
Just want to add another voice here confirming the race condition failures @filiprafalowicz has reproduced so well. Our Org has about ~100 test files and, after updating from version 0.10 to 0.12 and modifying test files to remove all the registerTestWireAdapters, we're getting inconsistent results when running all tests. Sometimes all tests pass with 100% coverage, sometimes we get a single failure (an uncaught Promise of I also created a Without a workaround, our team is currently blocked on updating our package to the latest version. |
Yea, after upgrading my sfdx project to v52 half of my tests suddenly broke and I am seeing data passed into the wrong wire method all over. |
…utomocks (#234) fixes #232 This PR reverts #231, #210, and #208. They provided functionality to auto mock apex methods. **Note:** Test authors will need to keep implementing their own apex (+apexContinuation) methods mocks after this PR is merged. * Revert "fix: apex automock should return a resolved promise (#231)" This reverts commit 544f95a. * Revert "fix: add apex stubs to the transformIgnorePatterns (#210)" This reverts commit fe8381a. * Revert "fix: automock apex methods with valid wire adapters (#208)" This reverts commit 125c689.
This issue is resolved in v0.12.6 (summer21) and v0.14.0 (next). Please note that apex and apexContinuation methods are no longer auto-mocked, and test authors will need to keep implementing their own mocks. FYI @filiprafalowicz, @BatemanVO, @yippie . |
Description
With auto-mock of Apex methods that was introduced and possibly module caching on jest side (https://github.com/facebook/jest/blob/master/packages/jest-resolve/src/resolver.ts#L158) if there is a parent and child component where child calls Apex imperatively mock from parent component will win and child component tests may fail. This occurs only if you run parent component test first and then child component test. If you run them in reverse order, all works fine as child component can mock the module properly.
Also, this caching also causes issues when the same module is imported in multiple unrelated components or when you use service components that only make Apex calls. If two components use the same service and tests are mocking its Apex call, there is a chance that mockResolvedValue or mockRejectedValue will work cross-tests meaning value we want to return in test for cmp1 will be returned in test for cmp2. This one is very hard to reproduce and happens when two test files are processed at the same time, but it is also related to the caching issue as they both share one instance of the mock.
Steps to Reproduce
cmpB (child component)
cmpA (parent component)
Command to reproduce the issue
# Running tests in serial mode to easily reproduce the issue sfdx-lwc-jest -- --runInBand --runTestsByPath ./force-app/main/default/lwc/cmpA/__tests__/cmpA.test.js ./force-app/main/default/lwc/cmpB/__tests__/cmpB.test.js
Note: issue happens only if cmpA test runs before cmpB test. I used this sample test sequencer to make sure that's the order:
And then, added this to jest.config.js:
Expected Results
Both tests are passing no matter what's the order.
Actual Results
If you run cmpB test first and then cmpA test, they both pass. If you change the order, only cmpA test passes.
Version
Additional context/Screenshots
I tried to use fix that is included in PR: #231 to see if it fixes the issue as I have also encountered the issue described there, but those two are unrelated. This one seems to be strictly related to the mocks caching and sharing instance of mock between all of the tests.
The text was updated successfully, but these errors were encountered: