Why Test EJS Templates?
Templates can break just like any other code. A missing variable, a typo in a tag, or a broken partial can crash your whole app. Testing your templates catches these problems early and gives you confidence when you refactor.
Testing EJS is straightforward — you render a template with some data and check the output. Let's walk through the patterns.
Basic Template Rendering in Tests
The simplest test renders a template and checks the HTML output. You need the ejs package and a test runner like Jest or Mocha.
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
function renderTemplate(name, data) {
const filePath = path.join(__dirname, 'views', name)
const template = fs.readFileSync(filePath, 'utf-8')
return ejs.render(template, data)
}
test('home page shows greeting', () => {
const html = renderTemplate('home.ejs', { name: 'Bilal' })
expect(html).toContain('Hello, Bilal')
})
This pattern reads the template file, renders it with data, and asserts the output contains what you expect. It's simple and effective for most cases.
Testing with Mock Data
Real apps pass complex objects to templates. In tests, create mock data that mirrors your production data shape.
test('product list renders all items', () => {
const products = [
{ id: 1, name: 'Widget', price: 9.99 },
{ id: 2, name: 'Gadget', price: 19.99 },
{ id: 3, name: 'Doohickey', price: 29.99 }
]
const html = renderTemplate('products.ejs', { products })
expect(html).toContain('Widget')
expect(html).toContain('Gadget')
expect(html).toContain('Doohickey')
expect((html.match(/product-card/g) || []).length).toBe(3)
})
You can also create a helper that generates realistic mock data so every test starts with consistent input.
Testing Partials
Partials are included in other templates. To test them, you can render the parent template that includes them, or render the partial directly.
test('header partial shows site name', () => {
const html = renderTemplate('partials/header.ejs', {
siteName: 'My Store'
})
expect(html).toContain('My Store')
expect(html).toContain(' {
const html = renderTemplate('partials/footer.ejs', {
year: 2026
})
expect(html).toContain('2026')
})
When testing partials that are included via <%- include() %>, test them through their parent template to make sure the data flows correctly.
Testing Conditional Logic
Templates often have if blocks and loops. Test both the true and false paths.
test('shows admin link only for admins', () => {
const adminHtml = renderTemplate('nav.ejs', { role: 'admin' })
expect(adminHtml).toContain('Admin Dashboard')
const userHtml = renderTemplate('nav.ejs', { role: 'user' })
expect(userHtml).not.toContain('Admin Dashboard')
})
test('shows message for empty cart', () => {
const html = renderTemplate('cart.ejs', { items: [] })
expect(html).toContain('Your cart is empty')
})
Always test edge cases — empty arrays, null values, and missing properties. Templates should handle them gracefully.
Jest Setup for EJS
Here's a minimal Jest configuration for testing EJS templates. Create a test file and a helper module.
const ejs = require('ejs')
const fs = require('fs')
const path = require('path')
const VIEWS_DIR = path.join(__dirname, '..', 'views')
function render(viewPath, data = {}) {
const fullPath = path.join(VIEWS_DIR, viewPath)
const template = fs.readFileSync(fullPath, 'utf-8')
return ejs.render(template, data, { filename: fullPath })
}
module.exports = { render }
Then in your test files, import the render function. The filename option lets EJS resolve includes and relative paths correctly.
Testing Error Handling
What happens when a required variable is missing? You can test that your templates fail gracefully or throw meaningful errors.
test('throws when required data is missing', () => {
expect(() => {
renderTemplate('profile.ejs', {})
}).toThrow()
})
test('handles undefined variables gracefully', () => {
const html = renderTemplate('page.ejs', {})
expect(html).toContain('No data available')
})
Some templates will throw if a variable is undefined and you access a property on it. Others might use defaults. Test both behaviors to match your expectations.
Try it Yourself →Snapshot Testing
Jest snapshot tests capture the rendered output and compare it against a saved version. If the output changes, the test fails and you review the diff.
test('login page matches snapshot', () => {
const html = renderTemplate('login.ejs', {
error: null
})
expect(html).toMatchSnapshot()
})
test('login page with error matches snapshot', () => {
const html = renderTemplate('login.ejs', {
error: 'Invalid credentials'
})
expect(html).toMatchSnapshot()
})
Snapshot tests are great for catching unintended layout changes. Run jest --updateSnapshot when you intentionally change the output.