Context missing functions in Enzyme test - unit-testing

So I'm using Enzyme to render a component with a bunch of context:
const surveysByType = "test";
const wrapper = shallow(
<Dashboard routeParams={{}} />,
{
context: {
assetUrl: () => {
return "https://www.example.com/broken.gif"
},
executeAction: () => {},
getStore: (whatever) => {
return {
getState: () => {
return {
_surveysByType: surveysByType,
};
},
on: () => {
return {};
}
}
},
router: {
createHref: () => {},
go: () => {},
goBack: () => {},
goForward: () => {},
isActive: () => {},
push: () => {},
replace: () => {},
setRouteLeaveHook: () => {},
},
siteUrl: () => {
return "https://www.example.com"
},
}
}
);
However, when I start trying to use said context, I find that only a single function is there, i.e.
console.log(wrapper.context())
yields
{ getStore: [Function: getStore] }
rather than all the functions. Indeed, if I try to do
expect(wrapper.context().assetUrl())
I get an error:
TypeError: wrapper.context(...).assetUrl is not a function
while this works fine:
expect(wrapper.context().getStore().getState())

What version of enzyme are you on?
Shallow takes an options param and there are some predefined options that it honors, but I don't see a context object: https://github.com/airbnb/enzyme/blob/master/src/shallow.js
How are you expecting the context object to be used exactly?

You probably need to provide more code to help diagnose this. Where is the console.log statement you quote above being called from, for example?
Making some assumptions, you should check what Dashboard.contextTypes contains. If it contains only getStore: PropTypes.function, that may explain why you only see that in the shallow render. If the other properties are used by components that wrap dashboard (e.g. a wrapper), the code where console.log is being called may not be getting the whole context you're passing into shallow(), because as I understand it, React only provides the objects from context that are defined in the component's contextTypes static method.

Related

vitest failed to mock quasar

I am having vue3 app with vite and vitest and trying to mock the Quasar useQuasar composable which I am using in my custom Composable like:
// useLoginRequestBuilder.ts
import { makeUserAuthentication } from "#/main/factories"
import { useQuasar } from "quasar"
export function useLoginRequestBuilder() {
const $q = useQuasar()
async function login() {
try {
$q.loading.show()
const auth = makeUserAuthentication()
return await auth.signinRedirect()
} catch (e) {
console.log(e)
$q.loading.hide()
$q.notify({
color: "red-4",
textColor: "white",
icon: "o_warning",
message: "Login Failed!",
})
}
}
return {
login,
}
}
and I am trying to mock quasar in tests like:
// useLoginRequestBuilder.spec.ts
import { useLoginRequestBuilder } from "#/main/builders"
vi.mock("quasar", () => ({ // <--- this is not really mocking quasar
useQuasar: () => ({
loading: {
show: () => true,
hide: () => true,
},
}),
}))
const spyAuth = vi.fn(() => Promise.resolve(true))
vi.mock("#/main/factories", () => ({
makeUserAuthentication: () => ({
signinRedirect: () => spyAuth(),
}),
}))
describe("test useLoginRequestBuilder", () => {
test("should call signinRedirect", async () => {
const { login } = useLoginRequestBuilder()
const sut = await login()
expect(sut).toBe(true)
})
})
vi.mock("quasar"... is failing to mock quasar and I am getting below error. That means, it failed to mock and failed to get the $q.loading.... object.
TypeError: Cannot read properties of undefined (reading 'loading')
I understand that there is a separate testing lib for quasar, here but I think this is not really the case here.
Bordering on a necro-post, but I had a similar issue that the mocking factory wasn't creating the plugins being used in non-Vue components, and had to mock each call individually in the end.
Though I'd add it here for anyone else
vitest.mock("quasar", () => vi.fn()); // this doesn't mock out calls
// use individual mocks as below
import { Loading } from "quasar";
vi.spyOn(Loading, "show").mockImplementation(() => vi.fn());
vi.spyOn(Loading, "hide").mockImplementation(() => vi.fn());

Override behavior of a stubbed function using JEST not working as expected

I have a test class that tests behavior of various HTTP methods in a Nest controller class. I am using Jest manual mocks to stub the behavior of various functions in the service class so that I do not have to rely on actual dependencies/services, eg. snowflake. I have a top level jest.mock() defined as follows which initializes the mocked version of the service class instead of the actual service class.The mocked service class is created inside mocks folder adjacent to the actual service class.
I am redefining the behavior of one of the mocked functions in the 'error scenario' describe block as shown in the code snippet below, for testing the error scenario . The test scenario : 'throws an error' is failing as it is still picking up the default mocked behavior. Any pointers or help is appreciated.
In short, I want to be able to define different mocked behavior for a single function of the same mocked class for various test scenarios.
Thanks
jest.mock('#modules/shipment-summary/shipment-summary.service');
describe('ShipmentSummaryController', () => {
let shipmentSummaryController: ShipmentSummaryController;
let shipmentSummaryService: ShipmentSummaryService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [],
controllers: [ShipmentSummaryController],
providers: [ShipmentSummaryService],
}).compile();
shipmentSummaryController = moduleRef.get<ShipmentSummaryController>(
ShipmentSummaryController,
);
shipmentSummaryService = moduleRef.get<ShipmentSummaryService>(
ShipmentSummaryService,
);
jest.clearAllMocks();
});
//All the tests inside this describe block work as expected
describe('valid shipment-mode scenario', () => {
describe('valid shipment modes for tenant', () => {
let modes: ShipmentMode[];
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
test('calls the service fn. with the correct arg', () => {
expect(shipmentSummaryService.getAvailableShipmentModes).toBeCalledWith(
'256',
);
});
test('all available shipment modes for 256 are returned', () => {
expect(modes).toEqual(validModeDropdown());
});
});
});
// redefining behavior of getAllshipmentModes() is not working
describe('error scenario', () => {
let modes: ShipmentMode[] = []
beforeEach(async () => {
modes = await shipmentSummaryController.getAllShipmentModes('256');
});
beforeAll(() => {
jest.clearAllMocks();
jest.mock('#modules/shipment-summary/shipment-summary.service.ts', () => {
return {
getAvailableShipmentModes: () => {
throw new Error('Test error');
},
}
});
});
test('throws an error', () => {
expect(() => shipmentSummaryController.getAllShipmentModes('256')).toThrow();
})
})
});
My mocked service class is as follows:
export const ShipmentSummaryService = jest.fn().mockReturnValue({
// Fn. to be mocked differently per test scenario.
getAvailableShipmentModes: jest.fn().mockResolvedValue(validModeDropdown()),
});
There are many ways of accomplishing this. The Nest docs outline a number of them. However, one of my preferred ways, useValue, is not as clear as it could be, so I'll added it here.
This example will also use jest in order to spy on a mock, changing its behavior depending on the test.
Imagine these two simple resources
Injectable();
export class SimpleService {
public sayHello(): string {
return "Hello, world!";
}
}
#Controller()
export class SimpleController {
constructor(
#Inject(SimpleService) private readonly simpleService: SimpleService
) {}
#Get()
public controllerSaysHello(): string {
return this.simpleService.sayHello();
}
}
Your tests could look something like this
describe("SimpleController", () => {
let controller: SimpleController;
const mockReturnValue = "Goodbye, world..",
mockSimpleService: SimpleService = {
sayHello: () => mockReturnValue,
};
beforeEach(() => {
jest.restoreAllMocks();
});
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
SimpleController,
{ provide: SimpleService, useValue: mockSimpleService },
],
}).compile();
controller = module.get(SimpleController);
});
test("default mockSimpleService", () => {
const result = controller.controllerSaysHello();
expect(result).toBe(mockReturnValue);
});
test("spied on mockSimpleService", () => {
const differentReturnValue = "Hallo!";
jest
.spyOn(mockSimpleService, "sayHello")
.mockReturnValue(differentReturnValue);
const result = controller.controllerSaysHello();
expect(result).toBe(differentReturnValue);
});
});

jest.spyOn() on factory function of jest.mock("path", factory function)

I am trying to spyOn a mocked factory function named getObject.
I am doing it like this:
import Storage from "/path/"
const fs1 = new Storage("ffffff");
const fs = mocked(fs1);
jest.mock("path to library", () => {
return {
CAStorage: function () {
return {
getObject: async (
_baseDirName: string,
_fileName?: string
): Promise<Buffer> => {
return Buffer.from(
`some string`
);
},
};
},
};
});
the spyOn code
describe("", () => {
test("", async () => {
let readFileSpy = jest.spyOn(fs, "getObject");
expect(readFileSpy).toBeCalledTimes(1);
});
});
The error I am getting is:
Expected number of calls: 1
Received number of calls: 0
even though the function have been called once.
Thanks for the help!
Take a look at this blog post
TL;DR
You should set the prototype of the mocked library / module.
e.g
import { ClassA } from "some/lib";
describe("description", () => {
test("test description", () => {
ClassA.prototype.functionToMock = jest.fn().mockImplementationOnce(() => {
// your implementation here
})
})
})

Check if a stubbed getter function has been called with sinon spy

I am using firebase admin and I am trying to write some unit tests for my code.
Since admin is injected in my function I figured I could mock a very simple object like this:
admin = {
get auth () {
return {
updateUser: () => {
return true;
},
createUser: () => {
return true;
},
getUser: () => {
throw Error('no user');
}
};
}
};
Then in a particular test I can stub the functions. Here is what I have done so far:
// stubbed functions
sinon.stub(admin, 'auth').get(() => () => ({
updateUser: () => ({ called: true }),
getUser: () => (userRecord),
createUser: () => ({ called: false })
}));
and those are working fine (I can see with my logs).
However in my test I would also want to check if createUser has been called at all.
I thought I could set up a spy on the createUser function, but so far I can't really get it to work.
Here is what I have been trying (with a bunch of variation always failing):
it.only('should update a user', async () => {
const userRecord = mockData
sinon.stub(admin, 'auth').get(() => () => ({
updateUser: () => ({ called: true }),
getUser: () => (userRecord),
createUser: () => ({ called: false })
}));
const spy = sinon.spy(admin, 'auth', ['get']); // this is not working
const user = await upsertUser(data, firestore, admin);
expect(user).toEqual(data.userDataForAuth); // this one is ok
sinon.assert.calledOnce(spy.get); // this throws an error
});
the bit of code I am trying to test (which is the upsert function is this:
// in my test exisiting user is not null (the stub `getUser` is returning a object
if (existingUser != null) {
try {
await admin.auth().updateUser(uid, userDataForAuth);
return userDataForAuth;
} catch (error) {
console.log('error', error);
throw Error('error updating user');
}
}
I am not even sure this is the best approach, happy to change it if there is a better one!

How can I pass a boolean to an expect statement for testing using mocha/chai?

Using Vue CLI I have a unit test that I am trying to check for a true/false that looks like this:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper).to.be.true
})
})
When I run npm run test:unit
I get the following:
AssertionError: expected { Object (isFunctionalComponent, _emitted, ...) } to be true
If I just check the value of available, then it's all good. But that seems like I'm doing it wrong.
Other tests I have written are working fine as I am checking for a text value:
describe('The thing', () => {
it('should have a name.', () => {
const name = 'Hal'
const wrapper = shallowMount(MyVueComponent, {
propsData: { name },
})
expect(wrapper.text()).to.include(name)
})
})
I am not sure how to check that the available is a boolean and it must be true. Any suggestions would be appreciated!
EDIT
This is what my Vue component looks like:
export default {
name: 'MyVueComponent',
props: {
name: String
},
data: function() {
return {
available: true,
}
},
}
EDIT 2
This seems to work in my unit test:
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.vm.available).to.be.true
})
})
However, it is looking at my actual component in my /src directory. If I change the data values from true to false my tests come out correctly. I'm not sure how to have the data stay at the test level. So if I were to change const available = false, my test should fail--but it does not.
EDIT 3
It seems like this works (to access the data object):
describe("The thing", () => {
it("must be available.", () => {
const defaultData = MyVueComponent.data();
// const wrapper = shallowMount(MyVueComponent, {});
expect(defaultData.available).to.be.true;
});
});
But it still seems not right that I'm referencing my actual code, and not sticking within the unit tests.
You want to check the received prop, which you can do with wrapper.props()
describe('The thing', () => {
it('must be available.', () => {
const available = true
const wrapper = shallowMount(MyVueComponent, {
propsData: { available },
})
expect(wrapper.props().available).to.be.true
// or:
// expect(wrapper.props().available).to.equal(available)
})
})
Chai's .to.be.true and .to.equal use === so there is no need to separately check that it is indeed a Boolean, but if you prefer the "expressiveness" of it, you can check it too:
expect(wrapper.props().available).to.be.a('boolean')