#[1]gnikyt feed [2]gnikyt Code ramblings. Prisma, Jest, and MySQL testing /* Mar 28, 2024 — 6.5KB */ This is an alternate extension to my previous post, [3]Shopify, Remix, Prisma, and Jest. Follow that post to get to its current state, and we’ll modify the existing files to support MySQL. Changes Change app/__mocks__/db.server.ts to: /* app/__mocks__/db.server.ts */ // eslint-disable-next-line import/no-extraneous-dependencies import { afterEach, beforeEach } from "@jest/globals"; import { PrismaClient } from "@prisma/client"; import { execSync } from "child_process"; import { existsSync } from "fs"; import * as path from "path"; /** * Run an NPM command. * * @param script - Script to run. */ function npm(script: string): Buffer { return execSync(["npm", "run", script].join(" "), { env: { ...process.env, NODE_ENV: "test", DATABASE_URL: process.env.DATABASE_URL, }, }); } /** * Seeds the database with initial data. */ function seedDatabase() { return Promise.allSettled([ // Seed shop session prisma.session.create({ data: { id: "example.myshopify.com_id", shop: "example.myshopify.com", accessToken: "token", state: "", isOnline: false, }, }), // Add more seed data ]); } // Path to generated types const typesPath = path.resolve("node_modules", ".prisma", "client", "index.d.ts" ); if (!existsSync(typesPath)) { npm("test:db:generate"); } // Prisma setup... generate a unique database for MySQL, override existing Prism a implementation via Jest mock const dbName = `test-${new Date().getTime()}`; process.env.DATABASE_URL = `${process.env.DATABASE_URL}/${dbName}`; const prisma = new PrismaClient({ datasources: { db: { url: process.env.DATABASE_URL } }, }); beforeEach(async () => { npm("test:db:deploy"); await seedDatabase(); }); afterEach(async () => { // Drop the database and disconnect await prisma.$executeRawUnsafe(`DROP DATABASE \`${dbName}\`;`); await prisma.$disconnect(); }); export default prisma; This will create a unique database in MySQL for each test, then destroy it. Example You reference the existing db.server.ts file normally in your tests, as Jest will swap it out with our __mocks__/db.server.ts version. /* app/order/processor.test.ts */ import { describe, expect, it } from "@jest/globals"; import db from "../db.server"; import { prismaHooks } from "../mocks.prisma"; import { ShopifyClient, ShopifyFixture } from "../mocks.clients"; // etc... /* just an example... */ describe("order processor", () => { it("should insert order", async () => { const orderId = OrderId("gid://shopify/Order/1"); const customerId = CustomerId("gid://shopify/Customer/1"); const product1 = ProductId("gid://shopify/Product/1"); const prodcut2 = ProductId("gid://shopify/Product/2"); // Job data const jobData = { order: { id: orderId.toInt(), admin_graphql_id: orderId.toGql(), tags: [], total_price: "115.00", customer: { id: customerId.toInt(), }, line_items: [ { product_id: p1.toInt(), price: "100.00", quantity: 1, }, { product_id: p2.toInt(), price: "15.00", quantity: 1, }, ], }, shop: "example.myshopify.com", }; // Shopify GraphQL mock client const client = ShopifyClient( ShopifyFixture("product-no-tag", "product/no-override-tag"), ShopifyFixture("product-with-tag", "product/with-override-tag"), ShopifyFixture("add-tag", "tags-add-customer"), ); // Run the processor const [retOrderId, retOustomerId, retSum]: JobReturn = await processor({ db, client, job: { data: jobData }, }); expect(retOrderId.isSame(orderId)).toBe(true); expect(retCustomerId.isSame(customerId)).toBe(true); expect(retSum.isSame(Cents(3000))); }); }); Usage You would need to pass in a database URL for connection parameters for MySQL, except for the database name, as we generate that normally: DATABASE_URL=mysql://root:password@localhost:3306 npm run test --runInBand Docker / Docker-Compose If you would like to utilize containers for the testing, this is doable as well. Create a Dockerfile-test and docker-compose.test.yml in your app root. # Dockerfile-test # Stage 1: Node modules installation FROM node:18-alpine as testcache WORKDIR /cache # Copy in package JSONs, and Prisma must be copied in before running install COPY package-lock.json . COPY package.json . COPY prisma ./prisma RUN npm install # Stage 2: Test FROM node:18-alpine as test WORKDIR /app EXPOSE 3000 # Set initial ENVs, can be overwritten in docker-compose.test.yml ENV NODE_ENV=test ENV DATABASE_URL=mysql://root:password@db:3306 # Copy in only files we need to complete the tests COPY app ./app COPY fixtures ./fixtures COPY jest.config.js ./jest.config.js COPY tsconfig.json ./tsconfig.json COPY --from=testcache /cache . CMD ["npm", "run", "test", "--", "--verbose", "--runInBand"] This Dockerfile uses Alpine Linux with two stage build… one to install dependencies and cache them, and a second step which uses the cached dependencies and runs the tests. version: '3.9' services: db: image: mysql:8.0 restart: always environment: MYSQL_ROOT_PASSWORD: "password" ports: - 3306:3306 expose: - 3306 volumes: - dbdata:/var/lib/mysql test: build: context: . dockerfile: Dockerfile-test depends_on: - db environment: - NODE_ENV=test - DATABASE_URL=mysql://root:password@db:3306 volumes: - ./jest.config.js:/app/jest.config.js - ./app:/app/app - ./prisma:/app/prisma volumes: dbdata: This Docker Compile file will setup MySQL v8 with a basic password and expose the port, as well as persist our database volume. It will also setup the app to test, mounting all needed files we need to run the tests. Then, docker-compose -f docker-compose.test.yml up --exit-code-from test to run it. The first time you run it, it may take time as the cache needs to be built, but after that its super fast in booting and starting the tests. The --exit-code-from test is needed so that Docker Compose will shut down once Jest exits. Without this, the container stays open and running. [4]MD | [5]TXT | [6]CC-4.0 __________________________________________________________________ [7]Ty King Ty King A self-taught, seasoned, and versatile developer from Newfoundland. Crafting innovative solutions with care and expertise. See more [8]about me. [9]Github [10]LinkedIn [11]CV [12]RSS * * * * * * * * * * References Visible links: 1. /rss.xml 2. / 3. /tmp/shopify-remix-prisma-and-jest 4. /prisma-jest-mysql-testing/index.md 5. /prisma-jest-mysql-testing/index.txt 6. https://creativecommons.org/licenses/by/4.0/ 7. /about 8. /about 9. https://github.com/gnikyt 10. https://linkedin.com/in/gnikyt 11. /assets/files/cv.pdf 12. /rss.xml Hidden links: 14. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-1 15. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-2 16. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-3 17. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-4 18. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-5 19. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-6 20. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-7 21. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-8 22. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-9 23. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-10 24. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-11 25. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-12 26. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-13 27. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-14 28. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-15 29. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-16 30. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-17 31. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-18 32. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-19 33. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-20 34. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-21 35. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-22 36. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-23 37. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-24 38. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-25 39. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-26 40. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-27 41. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-28 42. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-29 43. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-30 44. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-31 45. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-32 46. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-33 47. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-34 48. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-35 49. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-36 50. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-37 51. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-38 52. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-39 53. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-40 54. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-41 55. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-42 56. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-43 57. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-44 58. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-45 59. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-46 60. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-47 61. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-48 62. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-49 63. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-50 64. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-51 65. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-52 66. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-53 67. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-54 68. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-55 69. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-56 70. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-57 71. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-58 72. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-59 73. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-60 74. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-61 75. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-62 76. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-63 77. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-64 78. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-65 79. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-66 80. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-67 81. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb1-68 82. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-1 83. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-2 84. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-3 85. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-4 86. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-5 87. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-6 88. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-7 89. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-8 90. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-9 91. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-10 92. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-11 93. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-12 94. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-13 95. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-14 96. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-15 97. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-16 98. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-17 99. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-18 100. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-19 101. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-20 102. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-21 103. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-22 104. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-23 105. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-24 106. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-25 107. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-26 108. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-27 109. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-28 110. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-29 111. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-30 112. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-31 113. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-32 114. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-33 115. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-34 116. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-35 117. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-36 118. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-37 119. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-38 120. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-39 121. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-40 122. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-41 123. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-42 124. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-43 125. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-44 126. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-45 127. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-46 128. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-47 129. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-48 130. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-49 131. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-50 132. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-51 133. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-52 134. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-53 135. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-54 136. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-55 137. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-56 138. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-57 139. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-58 140. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-59 141. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-60 142. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb2-61 143. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-1 144. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-2 145. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-3 146. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-4 147. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-5 148. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-6 149. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-7 150. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-8 151. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-9 152. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-10 153. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-11 154. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-12 155. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-13 156. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-14 157. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-15 158. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-16 159. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-17 160. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-18 161. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-19 162. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-20 163. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-21 164. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-22 165. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-23 166. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-24 167. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-25 168. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-26 169. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-27 170. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-28 171. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-29 172. localhost/tmp/lynxXXXXxBsTP9/L757445-853TMP.html#cb4-30