我会以不同的方式处理这个问题。当您觉得需要模拟整个第三方库进行测试时,您的应用程序中出现了问题。
作为一般的最佳做法,您应该始终包装第三方库。初学者请查看this discussion。
基本上,这个想法是为所需功能定义自己的接口,然后使用第三方库实现这些接口。在您的其余代码中,您将只针对接口工作,而不针对第三方实现。
这有几个优点
- 您可以自己定义接口。它通常会比整个第三方库小得多,因为您很少使用该第三方库的所有功能,并且您可以决定针对具体用例的最佳接口定义,而不必完全遵循某些图书馆作者口授给你。
- 如果有一天您决定不再使用 MySQL,而是改用 Mongo,您可以编写一个 Mongo 实现 DB 接口。
- 就您而言,最重要的是:您可以轻松创建数据库接口的模拟实现,而无需开始模拟整个第三方 API。
那么这怎么可能呢?
首先,定义一个接口,因为它在您的代码中最有用。也许,您的 DB 接口可能如下所示:
interface Database<T> {
create(object: T): Promise<void>;
get(id: string): Promise<T>;
getAll(): Promise<T[]>;
update(id: string, object: T): Promise<void>;
delete(id: string): Promise<void>;
}
现在,您可以针对Database 接口开发整个代码库。当您需要从“表”中检索数据时,您可以使用 Database 实现,而不是在您的代码中编写 MySQL 查询。
我在这里仅举一个例子ResultRetriever,它非常原始,但可以达到目的:
class ResultRetriever {
constructor(private database: Database<Something>) {}
getResults(): Promise<Something[]> {
return this.database.getAll();
}
}
如您所见,您的代码不需要关心哪个数据库实现交付数据。另外,我们在这里inverted dependencies:ResultReteriver 被注入它的Database 实例。它不知道它得到了哪个具体的Database 实现。它不需要。它只关心它是否有效。
您现在可以轻松实现 MySQL Database 类:
class MySqlDatabase<T> implements Database<T> {
create(object: T): Promise<void> {...}
get(id: string): Promise<T> {...}
getAll(): Promise<T[]> {
const db = await mysql.createConnection(options);
const results = await db.execute('SELECT `something` from `table`;');
await db.end();
return results;
}
update(id: string, object: T): Promise<void> {...}
delete(id: string): Promise<void> {...}
}
现在我们已经从您的主要代码库中完全抽象出 MySQL 特定的实现。说到测试,可以写一个简单的MockDatabase:
export class MockDatabase<T> implements Database<T> {
private objects: T[] = [];
async create(object: T): Promise<void> {
this.objects.push(object);
}
async get(id: string): Promise<T> {
return this.objects.find(o => o.id === id);
}
async getAll(): Promise<T[]> {
return this.objects;
}
update(id: string, object: T): Promise<void> {...}
delete(id: string): Promise<void> {...}
}
在测试方面,您现在可以使用您的MockDatabase 测试您的ResultRetrieve,而不是依赖 MySQL 库并因此完全模拟它:
describe('ResultRetriever', () => {
let retriever: ResultRetriever;
let db: Database;
beforeEach(() => {
db = new MockDatabase();
retriever = new ResultRetriever(db);
});
...
});
很抱歉,如果我超出了问题的范围,但我觉得仅仅回应如何模拟 MySQL 库并不能解决底层架构问题。
如果您不使用/不想使用 TypeScript,则可以将相同的逻辑应用于 JavaScript。