I want to create a Mock API Server for my Jest tests so that I can define all my backend endpoints and create responses and authentication checks.
I have managed to set up the server and routes by following some of the source code from Chris Fritz "Vue-Enterprice-boilerplate":
https://github.com/chrisvfritz/vue-enterprise-boilerplate/tree/master/tests/unit
// jest.config.js
const _ = require("lodash");
process.env.MOCK_API_PORT = process.env.MOCK_API_PORT || _.random(9000, 9999);
module.exports = {
preset: "#vue/cli-plugin-unit-jest",
setupFiles: ["./tests/unit/setup"],
globalSetup: "<rootDir>/tests/unit/global-setup",
globalTeardown: "<rootDir>/tests/unit/global-teardown",
testMatch: ["**/(*.)spec.js"],
moduleFileExtensions: ["js", "jsx", "json", "vue"],
transform: {
"^.+\\.vue$": "vue-jest",
"^.+\\.js$": "babel-jest",
".+\\.(css|scss|jpe?g|png|gif|webp|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$":
"jest-transform-stub"
},
transformIgnorePatterns: ["/node_modules/(?!vue-spinner)"],
testURL: process.env.API_BASE_URL || `http://localhost:${process.env.MOCK_API_PORT}`
};
The server runs when the tests starts and I can console log the route files.
I just don't know how the axios call from my Vuex would go with the mock API instead of the real one.
Might need to import axios somewhere in the test to prevent the development URL to be used?
/tests/mock-api/routes/auth.js
const Users = require("../resources/users");
module.exports = app => {
console.log('I can see this during tests!');
app.post("/api/v1/login", async (req, res) => {
console.log("I don't see this..");
await Users.authenticate(req.body)
.then(user => {
res.json(user);
})
.catch(error => {
res.status(401).json({ message: error.message });
});
});
});
// /views/Login.spec.js
import Vue from "vue";
import Vuelidate from "vuelidate";
import Login from "#/views/Login";
import BaseButton from "#/components/Globals/_base-button.vue";
import BaseInput from "#/components/Globals/_base-input.vue";
import BaseLabel from "#/components/Globals/_base-label.vue";
import flushPromises from "flush-promises";
import store from "#/store";
import { shallowMount } from "#vue/test-utils";
Vue.use(Vuelidate);
describe("#/views/Login", () => {
// other tests..
it("redirects to posts on successful login", async () => {
const wrapper = shallowMount(Login, { store, stubs: { BaseInput, BaseButton, BaseLabel } });
wrapper.vm.$v.$touch();
const spyDispatch = jest.spyOn(wrapper.vm.$store, "dispatch");
const username = wrapper.find("#username");
const password = wrapper.find("#password");
username.element.value = "johndoe#email.com";
password.element.value = "passwordz";
username.trigger("input");
password.trigger("input");
await wrapper.find("#submitBtn").trigger("click.prevent");
await wrapper.vm.$nextTick();
await flushPromises();
await expect(spyDispatch).toHaveBeenCalledWith("auth/login", {
username: username.element.value,
password: password.element.value
});
// #TODO add expect for redirect as well
});
// /store/auth.js (vuex)
export const actions = {
async login({ commit }, { username, password }) {
console.log("I see this");
const response = await axios.post("/login",
{ username, password }, { withCredentials: true });
console.log("I don't see this");
// #TODO error handling
if (!response) return;
commit("setUser", { ...response.data.user });
router.push({ name: "Posts" });
},
The login action gets called but I don't get passed the axios.post.
Do I need to import axios somewhere to make sure I get a fresh instance? (Vuex uses one I set the baseURL and headers)
All the other tests and logic works except this.
Related
I need to switch out my backend in-memory DB for testing due to memory issues. Below is my code
import { fireEvent, render, screen, waitFor } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import App from "App";
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import { AccessLevel, ResponseApi, SystemUserApi } from "types";
let mock: MockAdapter;
beforeAll(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
beforeEach(() => {
jest.resetModules();
});
describe("<App />", () => {
test("login", async () => {
mock.onPost('/Hello').reply(200, getPost);
const result = render(<App />);
const user = userEvent.setup();
const btnLogin = screen.getByText(/Login/i) as HTMLButtonElement;
await userEvent.click(btnLogin);
let btnOk = screen.queryByText(/OK/i) as HTMLButtonElement;
expect(btnOk.disabled).toBe(true);
let btnCancel = screen.getByText(/Cancel/i) as HTMLButtonElement;
expect(btnCancel.disabled).toBe(false);
fireEvent.change(screen.getByLabelText(/Access Code/i) as HTMLInputElement, { target: { value: 'USER' } });
expect(btnOk.disabled).toBe(false);
await userEvent.click(btnOk);
//At this point I was expecting the onPost to be clicked
});
});
function getPost(config: any): any {
console.log(config);
debugger;
return {
data: {
access_code: 'USER'.toUpperCase(),
access_level: AccessLevel.USER ,
lock_level:true
} as SystemUserApi,
error: false,
} as ResponseApi
}
Deep down in the is a call axios post to /Hello but my function within the test is not called. I do not know if it has to do with the actual call being axios.request vs axios.post. I have tried switching to mock.onAny, but that did not seem to work. Not sure what to do here.
I'm trying to follow Ben Awad fullstack reddit clone tutorial.
using express-session I'm trying to send the client a cookie and userID each time they login - but when I do so the cookie is being set but the server stops responding from that client (either apollo studio or postman). If the cookie is deleted the server returns to function normally.
I have compared my code to others and could not find anything off.
what can I be missing ?
import { MikroORM } from "#mikro-orm/core";
import microConfig from "./mikro-orm.config";
import express from "express";
import { ApolloServer } from "apollo-server-express";
import { buildSchema } from "type-graphql";
import { HelloResolver } from "./resolvers/hello";
import { PostResolver } from "./resolvers/Post";
import { UserResolver } from "./resolvers/User";
import {__prod__} from "./constants";
import {createClient} from "redis";
import session from "express-session";
import connectRedis from "connect-redis";
import "reflect-metadata";
const main = async () => {
const orm = await MikroORM.init(microConfig);
await orm.getMigrator().up();
const app = express();
const RedisStore = connectRedis(session);
const redisClient = createClient();
await redisClient.connect();
const appSession = session({
name: "cid",
secret: "shhhh",
resave: false,
saveUninitialized: false,
store: new RedisStore({
client: redisClient,
})
,
cookie: {
maxAge: 1000 * 60 * 60 * 24,
secure: true,
httpOnly: false,
sameSite: "none",
},
})
app.use(appSession);
!__prod__ && app.set("trust proxy", 1);
app.set("Access-Control-Allow-Origin", "https://studio.apollographql.com");
app.set("Access-Control-Allow-Credentials", true)
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [HelloResolver, PostResolver, UserResolver],
validate: false,
}),
context: ({ res, req }) => ({ em: orm.em, res, req }),
introspection: !__prod__,
});
await apolloServer.start();
apolloServer.applyMiddleware({
app,
cors: {
origin: ["https://studio.apollographql.com"],
credentials: true,
},
});
app.get("/hello", (_, res) => {
res.send("Hello World");
});
app.listen(4000, () => {
console.log("server started on localhost:4000");
});
};
main().catch((err) => {
console.error(err);
})
Problem was that legacyMode needs to be set to true in createClient for redis to work properly with connect-redis :)
I'm using Jest and axios-mock-adapter to write tests for my API services. The problem is that when I run the test I get an error stating:
Error: unable to verify the first certificate.
app.service.js is the following
import ApiService from '#/services/api.service'
export default {
async loadDashboard (psRef) {
let result = await ApiService.get('user/' + psRef + '/dashboard')
.catch(error => {
console.error(error)
})
return result.data
}
}
api.service.js is where I create my axios instance like so
import Axios from 'axios'
const baseDomain = process.env.VUE_APP_BACKEND
const baseURL = `${baseDomain}${process.env.VUE_APP_API}`
export default Axios.create({
baseURL: baseURL,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
The test is the following:
const baseDomain = process.env.VUE_APP_BACKEND
const baseURL = `${baseDomain}${process.env.VUE_APP_API}`
test('loadDashboard should return the dashboard data for the user', async () => {
mock.onGet(`${baseURL}user/85/dashboard`).reply(200, { dashBoardData })
let response = await AppService.loadDashboard(85)
expect(response).toEqual(dashBoardData)
// AppService.loadDashboard(85).then(response => {
// expect(response.data).toEqual(dashBoardData)
// })
})
Does anybody know how to fix this error?
I have a service that uses a custom axios instance that I am trying to test but I keep getting an error.
Here is the error:
: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
Here is the test:
import moxios from 'moxios';
import NotificationService, { instance } from '../NotificationService';
beforeEach(() => {
moxios.install(instance);
});
afterEach(() => {
moxios.uninstall(instance);
});
const fetchNotifData = {
data: {
bell: false,
rollups: []
}
};
describe('NotificationService.js', () => {
it('returns the bell property', async done => {
const isResolved = true;
const data = await NotificationService.fetchNotifications(isResolved);
moxios.wait(() => {
let request = moxios.requests.mostRecent();
console.log(request);
request
.respondWith({
status: 200,
response: fetchNotifData
})
.then(() => {
console.log(data);
expect(data).toHaveProperty('data.bell');
done();
});
});
});
});
And here is the code that I'm trying to test:
import axios from 'axios';
// hardcoded user guid
const userId = '8c4';
// axios instance with hardcoded url and auth header
export const instance = axios.create({
baseURL: 'hidden',
headers: {
Authorization:
'JWT ey'
});
/**
* Notification Service
* Call these methods from the Notification Vuex Module
*/
export default class NotificationService {
/**
* #GET Gets a list of Notifications for a User
* #returns {AxiosPromise<any>}
* #param query
*/
static async fetchNotifications(query) {
try {
const res = await instance.get(`/rollups/user/${userId}`, {
query: query
});
console.log('NotificationService.fetchNotifications()', res);
return res;
} catch (error) {
console.error(error);
}
}
}
I've tried shortening the jest timeout and that did not work. I think it is moxios not installing the axios instance properly, but I can't find any reason why it wouldn't.
Any help is appreciated, thanks in advance.
have you tried changing the Jest environment settings by adding this to your test file?
/**
* #jest-environment node
*/
import moxios from 'moxios';
...
Jest tends to prevent the requests from going out unless you add that. Either way, I use nock instead of moxios and I recommend it.
I want to run an in-memory database for my tests, but I'm unable to make my application connect to it when I run npm test.
When I run npm test i get:
Connection fails: Error: ER_ACCESS_DENIED_ERROR: Access denied for user ''#'172.21.0.1' (using password: NO)
This is happening because I'm not setting any env variables on npm test, but I don't want to use MySQL on my tests and just an in-memory database, here what I have.
testdb.datasources.ts
import {juggler} from '#loopback/repository';
export const testdb: juggler.DataSource = new juggler.DataSource({
name: 'testdb',
connector: 'memory',
});
country.controller.acceptance.ts
import {Client, expect} from '#loopback/testlab';
import {MyApplication} from '../..';
import {setupApplication} from './test-helper';
import {givenEmptyDatabase} from '../helpers/database.helpers';
describe('Country Controller', () => {
let app: MyApplication;
let client: Client;
before('setupApllication', async () => {
({app, client} = await setupApplication());
});
before(givenEmptyDatabase);
// before(givenRunningApp);
after(async () => {
await app.stop();
});
it('Should count 0 countries', async () => {
const res = await client.get('/countries/count').expect(200);
//assertations
expect(res.body.count).to.equal(0);
});
});
test-helper.ts
import {MyApplication} from '../..';
import {
createRestAppClient,
givenHttpServerConfig,
Client,
} from '#loopback/testlab';
import {testdb} from '../fixtures/datasources/testdb.datasource';
export async function setupApplication(): Promise<AppWithClient> {
const app = new MyApplication({
rest: givenHttpServerConfig({host: 'localhost'}),
});
app.dataSource(testdb); // <--- Hoped this would do the job
await app.boot();
await app.start();
const client = createRestAppClient(app);
return {app, client};
}
export interface AppWithClient {
app: MyApplication;
client: Client;
}
The Country controller is just a standard controller create using lb4 controller.
#get('/countries/count', {
responses: {
'200': {
description: 'country model count',
content: {'application/json': {schema: countschema}},
},
},
})
async count(
#param.query.object('where', getwhereschemafor(country)) where?: where,
): promise<count> {
return await this.countryrepository.count(where);
}
Any ideas on whats going wrong?
i Found this way that works for me
Change this:
app.dataSource(testdb);
To this:
await app.bind('datasource.config.db').to({
name: 'db',
connector: 'memory'
});
You are just changing the configurations of datasource to use a local database but is still the same datasource!
make sure that the string of bind is the same used in your main datasource!