
///mongoose/weather/__mocks__/model.ts
import { projectUpdateInfoSubscribe } from "next/dist/build/swc/generated-native";
import { WeatherInterface } from "../interface";
type param = {
[key: string]: string;
};
const WeatherModel = {
create: jest.fn((newData: WeatherInterface) => Promise.resolve(true)),
findOne: jest.fn(({ zip: paramZip }: param) => Promise.resolve(true)),
updateOne: jest.fn(({ zip: paramZip }: param, newData: WeatherInterface) => Promise.resolve(true)),
deleteOne: jest.fn(({ zip: paramZip }: param) => Promise.resolve(true))
};
export default WeatherModel;
jest.fn(): Jest provides a mock function that replaces the real function so that you can monitor its behavior, for example:
- Has it ever been called?
- How many times have you been called?
- What parameters are passed in when it is called?
- You can let it return the value you specify

//interface.ts
export declare interface WeatherInterface {
zip: string;
weather: string;
tempC: string;
tempF: string;
friends: string[];
}
//schema.ts
import { Schema } from "mongoose";
import { WeatherInterface } from "./interface";
export const WeatherSchema = new Schema<WeatherInterface>({
zip: {
type: String,
required: true,
},
weather: {
type: String,
required: true,
},
tempC: {
type: String,
required: true,
},
tempF: {
type: String,
required: true,
},
friends: {
type: [String],
required: true,
}
});;
//model.ts
import mongoose, { model } from "mongoose";
import { WeatherInterface } from "./interface";
import { WeatherSchema } from "./schema";
export default mongoose.models.Weather ||
model<WeatherInterface>("Weather", WeatherSchema);
//service.ts
import WeatherModel from "./model";
import { WeatherInterface } from "./interface";
export async function storeDocument(doc: WeatherInterface): Promise<boolean> {
try {
await WeatherModel.create(doc);
} catch (error) {
return false;
}
return true;
}
export async function findByZip(paramZip: string): Promise<Array<WeatherInterface> | null> {
try {
return await WeatherModel.findOne({zip: paramZip});
} catch (error) {
console.log(error);
}
return [];
}
export async function updateByZip(paramZip: string, newData: WeatherInterface): Promise<boolean> {
try {
await WeatherModel.updateOne({zip: paramZip}, newData);
return true;
} catch (error) {
console.log(error);
}
return false;
}
export async function deleteByZip(paramZip: string): Promise<boolean> {
try {
await WeatherModel.deleteOne({zip: paramZip});
return true;
} catch (error) {
console.log(error);
}
return false;
}
//db-connect.ts
import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
import { storeDocument } from "@/mongoose/weather/services";
let isConnected: boolean = false; // 用來追蹤是否已經有連線
async function dbConnect(): Promise<any | string> {
// 如果已經連接過了,就直接返回已經建立的連線
if (isConnected) {
return mongoose.connection;
}
// 如果尚未建立連線,則創建新的連線
try {
const mongoServer = await MongoMemoryServer.create();
const MONGO_URI = mongoServer.getUri();
await mongoose.disconnect(); // 確保任何現有的連線已經斷開
await mongoose.connect(MONGO_URI, {
dbName: "Weather"
});
await storeDocument({
zip: "96815",
weather: "sunny",
tempC: "25C",
tempF: "70F",
friends: ["96814", "96826"]
});
await storeDocument({
zip: "96814",
weather: "rainy",
tempC: "20C",
tempF: "68F",
friends: ["96815", "96826"]
});
await storeDocument({
zip: "96826",
weather: "rainy",
tempC: "30C",
tempF: "86F",
friends: ["96815", "96814"]
});
isConnected = true; // 設置連線成功的標誌
mongoose.connection.on("disconnected", () => {
isConnected = false;
});
return mongoServer;
} catch (error) {
throw error;
}
}
export default dbConnect;

//db-connect.test.ts
import dbConnect from "@/middleware/db-connect";
import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
describe("dbConnect", () => {
let connection: any;
afterEach(async () => {
jest.clearAllMocks();
await connection.stop();
await mongoose.disconnect();
});
afterAll(async () => {
jest.restoreAllMocks();
})
test("calls MongoMemoryServer.create()", async () => {
const spy = jest.spyOn(MongoMemoryServer, "create");
connection = await dbConnect();
expect(spy).toHaveBeenCalled();
})
test("calls mongoose.disconnect()", async () => {
const spy = jest.spyOn(mongoose, "disconnect");
connection = await dbConnect();
expect(spy).toHaveBeenCalled();
})
test("calls mongoose.connect()", async () => {
const spy = jest.spyOn(mongoose, "connect");
connection = await dbConnect();
const MONGO_URI = connection.getUri();
expect(spy).toHaveBeenCalledWith(MONGO_URI, { dbName: "Weather" });
})
})
//services.test.ts
import { WeatherInterface } from "@/mongoose/weather/interface";
import {
findByZip,
storeDocument,
updateByZip,
deleteByZip
} from "@/mongoose/weather/services";
import WeatherModel from "@/mongoose/weather/model";
jest.mock("@/mongoose/weather/model");
describe("the weather services", () => {
let doc: WeatherInterface = {
zip: "test",
weather: "weather",
tempC: "00",
tempF: "01",
friends: []
};
afterEach(async () => {
jest.clearAllMocks();
})
afterAll(async () => {
jest.restoreAllMocks();
})
describe("API storeDocument", () => {
test("returns true", async () => {
const result = await storeDocument(doc);
expect(result).toBeTruthy();
})
test("passes the document to Model.create", async () => {
const spy = jest.spyOn(WeatherModel, "create");
await storeDocument(doc);
expect(spy).toHaveBeenCalledWith(doc);
})
})
describe("API findByZip", () => {
test("returns true", async () => {
const result = await findByZip(doc.zip);
expect(result).toBeTruthy();
})
test("passes the zip code to Model.findOne()", async () => {
const spy = jest.spyOn(WeatherModel, "findOne");
await findByZip(doc.zip);
expect(spy).toHaveBeenCalledWith({ zip: doc.zip });
})
})
describe("API updateByZip", () => {
test("returns true", async () => {
const result = await updateByZip(doc.zip, doc);
expect(result).toBeTruthy();
})
test("passes the zip code and new data to Model.updateOne()", async () => {
const spy = jest.spyOn(WeatherModel, "updateOne");
await updateByZip(doc.zip, doc);
expect(spy).toHaveBeenCalledWith({ zip: doc.zip }, doc);
})
})
describe("API deleteByZip", () => {
test("returns true", async () => {
const result = await deleteByZip(doc.zip);
expect(result).toBeTruthy();
})
test("passes the zip code to Model.deleteOne()", async () => {
const spy = jest.spyOn(WeatherModel, "deleteOne");
await deleteByZip(doc.zip);
expect(spy).toHaveBeenCalledWith({ zip: doc.zip });
})
})
});
