【问题标题】:How to resolve JEST error duplicate key value violates unique constraint "users_pkey"如何解决 JEST 错误重复键值违反唯一约束“users_pkey”
【发布时间】:2021-03-29 05:27:04
【问题描述】:

我正在使用 JEST 编写测试,但我的清晰数据库部分似乎不起作用。 我总是收到以下错误:

study-service_1  | server-0   | info: Connection opened
postgres_1       | 2020-12-18 14:35:51.993 UTC [98] ERROR:  duplicate key value violates unique constraint "sites_pkey"
postgres_1       | 2020-12-18 14:35:51.993 UTC [98] DETAIL:  Key (id)=(3) already exists.
postgres_1       | 2020-12-18 14:35:51.993 UTC [98] STATEMENT:  INSERT INTO sites (id, name, address, city, country_code, study_id, site_id, status, timezone, "default") VALUES(3, 'site 3','address', 'city', 'dk', 'DEFAULT1', 'site 3', 'ACTIVE', 'Europe/Rome', false)
study-service_1  |   console.error node_modules/jest-jasmine2/build/jasmine/Env.js:157
study-service_1  |     Unhandled error
study-service_1  |
study-service_1  |   console.error node_modules/jest-jasmine2/build/jasmine/Env.js:158
study-service_1  |     error: duplicate key value violates unique constraint "sites_pkey"
study-service_1  |         at Connection.Object.<anonymous>.Connection.parseE (/test/node_modules/pg/lib/connection.js:567:11)
study-service_1  |         at Connection.Object.<anonymous>.Connection.parseMessage (/test/node_modules/pg/lib/connection.js:391:17)
study-service_1  |         at Socket.<anonymous> (/test/node_modules/pg/lib/connection.js:129:22)
study-service_1  |         at Socket.emit (events.js:198:13)
study-service_1  |         at addChunk (_stream_readable.js:288:12)
study-service_1  |         at readableAddChunk (_stream_readable.js:269:11)
study-service_1  |         at Socket.Readable.push (_stream_readable.js:224:10)
study-service_1  |         at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)

我无法理解问题出在哪里,我在测试中进行了此数据设置

const sitesData = [
  {
    id: 11,
    name: "site 1",
    address: "address",
    city: "city",
    country_code: "es",
    study_id: "DEFAULT1",
    site_id: "site 1",
    status: "ACTIVE",
    timezone: "Europe/Rome",
    default: true,
  },
  {
    id: 2,
    name: "site 2",
    address: "address",
    city: "city",
    country_code: "es",
    study_id: "DEFAULT1",
    site_id: "site 2",
    status: "INACTIVE",
    timezone: "United States/New York",
    default: false,
  },
  {
    id: 3,
    name: "site 3",
    address: "address",
    city: "city",
    country_code: "dk",
    study_id: "DEFAULT1",
    site_id: "site 3",
    status: "ACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
  {
    id: 4,
    name: "site 4",
    address: "address",
    city: "city",
    country_code: "dk",
    study_id: "DEFAULT1",
    site_id: "site 4",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
  {
    id: 5,
    name: "site 5",
    address: "address",
    city: "city",
    country_code: "pl",
    study_id: "DEFAULT1",
    site_id: "site 5",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
];

const sitesData2 = [
  {
    id: 6,
    name: "site 6",
    address: "address",
    city: "city",
    country_code: "es",
    study_id: "DEFAULT2",
    site_id: "site 6",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
  {
    id: 7,
    name: "site 7",
    address: "address",
    city: "city",
    country_code: "it",
    study_id: "DEFAULT2",
    site_id: "site 7",
    status: "INACTIVE",
    timezone: "United States/New York",
    default: true,
  },
  {
    id: 8,
    name: "site 8",
    address: "address",
    city: "city",
    country_code: "dk",
    study_id: "DEFAULT2",
    site_id: "site 8",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
  {
    id: 9,
    name: "site 9",
    address: "address",
    city: "city",
    country_code: "dk",
    study_id: "DEFAULT2",
    site_id: "site 9",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
  {
    id: 10,
    name: "site 10",
    address: "address",
    city: "city",
    country_code: "pl",
    study_id: "DEFAULT2",
    site_id: "site 10",
    status: "INACTIVE",
    timezone: "Europe/Rome",
    default: false,
  },
]

const serviceWithDAO = (daoMocks) => {
  return SiteService({
    withDAO: block => (
      block(daoMocks)
    ),
  });
}

const Data = {
  data: [],
  get value() {
    return this.data;
  },
  set value(v) {
    this.data = v;
  },
  get mock() {
    return {
      select: jest.fn(({ studyId }) => {
        return Promise.resolve(this.data.filter((i) => i.study_id === studyId));
      }),
    };
  },
};


async function clearDatabase() {
  await Promise.all([
    dbClient.query("DELETE FROM studies WHERE id='DEFAULT1'"),
    ...sitesData.map((site) =>
      dbClient.query(`DELETE FROM sites WHERE site_id='${site.id}'`)
    ),
  ],
  [
    dbClient.query("DELETE FROM studies WHERE id='DEFAULT2'"),
    ...sitesData2.map((site) =>
      dbClient.query(`DELETE FROM sites WHERE site_id='${site.id}'`)
    ),
  ]);
  dbClient.disconnect();
}

async function initializeDatabase() {
  await dbClient.connect();
  await Promise.all([
    dbClient.query("DELETE FROM studies WHERE id='DEFAULT1'"),
    ...sitesData.map((site) =>
      dbClient.query(`DELETE FROM sites WHERE site_id='${site.id}'`)
    ),
  ],[
    dbClient.query("DELETE FROM studies WHERE id='DEFAULT2'"),
    ...sitesData2.map((site) =>
      dbClient.query(`DELETE FROM sites WHERE site_id='${site.id}'`)
    ),
  ]);

  await Promise.all(
    [
      dbClient.query("INSERT INTO studies (id, internal_study_name, start_date) values ('DEFAULT1', 'Test DEFAULT SITE', '2030-10-10T08:00:00.000Z')")
    ],
    [
      dbClient.query("INSERT INTO studies (id, internal_study_name, start_date) values ('DEFAULT2', 'Test DEFAULT SITE 2', '2030-10-10T08:00:00.000Z')")
    ]
  );

  await Promise.all(
    [
      sitesData.map(site => dbClient.query(
       `INSERT INTO sites (id, name, address, city, country_code, study_id, site_id, status, timezone, "default") VALUES(${site.id}, '${site.name}','${site.address}', '${site.city}', '${site.country_code}', '${site.study_id}', '${site.site_id}', '${site.status}', '${site.timezone}', ${site.default})`)),
    ],
    [
      sitesData2.map(site => dbClient.query(
        `INSERT INTO sites (id, name, address, city, country_code, study_id, site_id, status, timezone, "default") VALUES(${site.id}, '${site.name}','${site.address}', '${site.city}', '${site.country_code}', '${site.study_id}', '${site.site_id}', '${site.status}', '${site.timezone}', ${site.default})`)),

    ]);
  const { rows: input } = await dbClient.query(`SELECT name, address, study_id, "default", country_code, site_id, status FROM sites`);
  Data.value = input;
}

beforeAll(() => {
  return initializeDatabase();
});

afterAll(() => {
  return clearDatabase();
});

此数据用作我正在运行的测试的基本设置

describe('Default Site', async () => {
  test('Undefined no sites of a study', async () => {
    const service = serviceWithDAO({ sites: Data.mock});
    console.log(Data.value);
    const defaultSite = await service.getDefaultSite({ studyId: 'DEF001', locale: 'us_ES'});
    console.log('DEFAULT SITE',defaultSite);
    expect(defaultSite).toBeUndefined();
  });

  test('C1 - is default, same country, active', async () => {
    const service = serviceWithDAO({ sites: Data.mock});
    const defaultSite = await service.getDefaultSite({ studyId: 'DEFAULT1', locale: 'us_ES'});
    console.log('C1', defaultSite);
    expect(defaultSite.default).toBeTruthy();
    expect(defaultSite.country_code).toEqual('es');
    expect(defaultSite.status).toEqual('ACTIVE');
    expect(defaultSite.site_id).toEqual('site 1');
  });

  test('C2 - is default, same country, inactive', async () => {
    const service = serviceWithDAO({ sites: Data.mock});
    const defaultSite = await service.getDefaultSite({ studyId: 'DEFAULT2', locale: 'us_IT'});
    console.log('C2', defaultSite);
    expect(defaultSite.default).toBeTruthy();
    expect(defaultSite.country_code).toEqual('it');
    expect(defaultSite.status).toEqual('INACTIVE');
    expect(defaultSite.site_id).toEqual('site 7');
  });

我试图在我的承诺中删除和清除数据库,但似乎有些东西不起作用。

【问题讨论】:

    标签: javascript node.js postgresql jestjs


    【解决方案1】:

    您可以尝试在 beforeAll 挂钩中同时进行清除和初始化,如果问题是由先前的测试运行失败引起的,这可能会有所帮助:

    beforeAll(async () => {
      await clearDatabase();
      await initializeDatabase();
    });
    
    afterAll(() => {
      await clearDatabase();
    });
    

    另外,在测试中处理数据库的一个好方法是为每次测试运行创建一个干净的数据库。 @databases/pg-test 库可以帮助您为 Jest 进行设置。这将是一个完全干净的数据库,因此您需要先创建表,然后才能在测试中与它们进行实际交互。

    【讨论】:

    • 不幸的是这并没有解决我的问题
    • 在这种情况下,您可能只是在测试中插入违反约束的数据,或者在调用 clearDatabase 时未能清除您需要的所有数据。我建议您查看插入网站的所有查询,并检查它们是否没有多次指定相同的 id。
    猜你喜欢
    • 2016-10-24
    • 2020-02-15
    • 2016-07-27
    • 2011-10-17
    • 2012-03-03
    • 2018-08-26
    • 2021-10-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多