or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

executors.mdgenerators.mdindex.mdjson-utilities.mdpackage-management.mdplugins.mdproject-configuration.mdproject-graph.mdstring-path-utilities.mdtesting-utilities.mdtree-filesystem.md

testing-utilities.mddocs/

0

# Testing Utilities

1

2

Testing helpers for creating virtual workspaces and file systems for generator and executor testing. These utilities enable comprehensive testing of Nx generators, executors, and other workspace operations in isolated environments.

3

4

## Capabilities

5

6

### Tree Creation for Testing

7

8

Functions for creating virtual file systems in test environments.

9

10

```typescript { .api }

11

/**

12

* Create an empty virtual file system tree

13

* @returns Empty Tree instance for testing

14

*/

15

function createTree(): Tree;

16

17

/**

18

* Create a virtual tree with a basic empty workspace structure

19

* @returns Tree with minimal workspace files (nx.json, package.json, etc.)

20

*/

21

function createTreeWithEmptyWorkspace(): Tree;

22

23

/**

24

* @deprecated Use createTreeWithEmptyWorkspace instead

25

* Create a virtual tree with empty V1 workspace (throws error)

26

*/

27

function createTreeWithEmptyV1Workspace(): never;

28

```

29

30

**Usage Examples:**

31

32

```typescript

33

import {

34

createTree,

35

createTreeWithEmptyWorkspace,

36

Tree

37

} from "@nrwl/devkit/testing";

38

39

describe('My Generator', () => {

40

let tree: Tree;

41

42

beforeEach(() => {

43

// Create empty workspace for testing

44

tree = createTreeWithEmptyWorkspace();

45

});

46

47

it('should generate files correctly', async () => {

48

// Run your generator

49

await myGenerator(tree, { name: 'test-lib' });

50

51

// Assert files were created

52

expect(tree.exists('libs/test-lib/src/index.ts')).toBe(true);

53

expect(tree.exists('libs/test-lib/project.json')).toBe(true);

54

55

// Check file contents

56

const indexContent = tree.read('libs/test-lib/src/index.ts', 'utf-8');

57

expect(indexContent).toContain('export');

58

});

59

60

it('should update existing files', async () => {

61

// Pre-populate tree with existing files

62

tree.write('package.json', JSON.stringify({

63

name: 'test-workspace',

64

scripts: {}

65

}));

66

67

// Run generator that modifies existing files

68

await myGenerator(tree, { name: 'test-lib' });

69

70

// Check modifications

71

const packageJson = JSON.parse(tree.read('package.json', 'utf-8')!);

72

expect(packageJson.scripts).toBeDefined();

73

});

74

});

75

```

76

77

### Testing Patterns

78

79

Common patterns for testing generators and executors.

80

81

```typescript { .api }

82

/**

83

* Helper to run generator and capture results

84

* @param generator - Generator function to test

85

* @param tree - Virtual file system

86

* @param options - Generator options

87

* @returns Promise with generator result and updated tree

88

*/

89

async function runGenerator<T>(

90

generator: Generator<T>,

91

tree: Tree,

92

options: T

93

): Promise<{

94

tree: Tree;

95

callback?: GeneratorCallback;

96

}>;

97

98

/**

99

* Helper to test executor execution

100

* @param executor - Executor function to test

101

* @param options - Executor options

102

* @param context - Mock executor context

103

* @returns Promise with execution result

104

*/

105

async function runExecutor<T>(

106

executor: Executor<T>,

107

options: T,

108

context: Partial<ExecutorContext>

109

): Promise<{ success: boolean; [key: string]: any }>;

110

```

111

112

**Usage Examples:**

113

114

```typescript

115

import {

116

Tree,

117

Generator,

118

ExecutorContext,

119

createTreeWithEmptyWorkspace

120

} from "@nrwl/devkit";

121

import { runGenerator, runExecutor } from "@nrwl/devkit/testing";

122

123

// Testing a generator

124

describe('Library Generator', () => {

125

let tree: Tree;

126

127

beforeEach(() => {

128

tree = createTreeWithEmptyWorkspace();

129

});

130

131

it('should create library files', async () => {

132

const { tree: resultTree, callback } = await runGenerator(

133

libraryGenerator,

134

tree,

135

{ name: 'my-lib', directory: 'libs' }

136

);

137

138

// Test file creation

139

expect(resultTree.exists('libs/my-lib/src/index.ts')).toBe(true);

140

expect(resultTree.exists('libs/my-lib/project.json')).toBe(true);

141

142

// Test project configuration

143

const projectJson = JSON.parse(

144

resultTree.read('libs/my-lib/project.json', 'utf-8')!

145

);

146

expect(projectJson.projectType).toBe('library');

147

148

// Test callback execution

149

if (callback) {

150

await callback();

151

// Verify callback effects

152

}

153

});

154

155

it('should handle existing files gracefully', async () => {

156

// Pre-create conflicting file

157

tree.write('libs/my-lib/src/index.ts', 'existing content');

158

159

await expect(

160

runGenerator(libraryGenerator, tree, { name: 'my-lib' })

161

).rejects.toThrow('File already exists');

162

});

163

});

164

165

// Testing an executor

166

describe('Build Executor', () => {

167

it('should build successfully', async () => {

168

const mockContext: Partial<ExecutorContext> = {

169

root: '/workspace',

170

projectName: 'my-app',

171

targetName: 'build',

172

workspace: {

173

projects: {

174

'my-app': {

175

root: 'apps/my-app'

176

}

177

}

178

}

179

};

180

181

const result = await runExecutor(

182

buildExecutor,

183

{ outputPath: 'dist/apps/my-app' },

184

mockContext

185

);

186

187

expect(result.success).toBe(true);

188

});

189

190

it('should handle build errors', async () => {

191

const mockContext: Partial<ExecutorContext> = {

192

root: '/workspace',

193

projectName: 'broken-app'

194

};

195

196

const result = await runExecutor(

197

buildExecutor,

198

{ outputPath: 'dist/apps/broken-app' },

199

mockContext

200

);

201

202

expect(result.success).toBe(false);

203

expect(result.error).toBeDefined();

204

});

205

});

206

```

207

208

### Mock Utilities

209

210

Functions for creating mock objects and contexts for testing.

211

212

```typescript { .api }

213

/**

214

* Create a mock executor context for testing

215

* @param overrides - Properties to override in the context

216

* @returns Mock ExecutorContext

217

*/

218

function createMockExecutorContext(

219

overrides?: Partial<ExecutorContext>

220

): ExecutorContext;

221

222

/**

223

* Create a mock project graph for testing

224

* @param projects - Projects to include in the graph

225

* @returns Mock ProjectGraph

226

*/

227

function createMockProjectGraph(

228

projects: Record<string, ProjectConfiguration>

229

): ProjectGraph;

230

231

/**

232

* Create mock workspace configuration

233

* @param projects - Projects to include

234

* @returns Mock workspace configuration

235

*/

236

function createMockWorkspace(

237

projects: Record<string, ProjectConfiguration>

238

): ProjectsConfigurations;

239

```

240

241

**Usage Examples:**

242

243

```typescript

244

import {

245

createMockExecutorContext,

246

createMockProjectGraph,

247

createMockWorkspace

248

} from "@nrwl/devkit/testing";

249

250

describe('Workspace Operations', () => {

251

it('should work with mock project graph', () => {

252

const mockGraph = createMockProjectGraph({

253

'app1': {

254

root: 'apps/app1',

255

projectType: 'application'

256

},

257

'lib1': {

258

root: 'libs/lib1',

259

projectType: 'library'

260

}

261

});

262

263

// Test functions that depend on project graph

264

const dependencies = findProjectDependencies(mockGraph, 'app1');

265

expect(dependencies).toEqual(['lib1']);

266

});

267

268

it('should work with mock executor context', async () => {

269

const mockContext = createMockExecutorContext({

270

projectName: 'test-project',

271

targetName: 'build',

272

root: '/test-workspace'

273

});

274

275

const result = await myExecutor({ outputPath: 'dist' }, mockContext);

276

expect(result.success).toBe(true);

277

});

278

});

279

```

280

281

### Test Helpers for Common Scenarios

282

283

Specialized utilities for testing common Nx scenarios.

284

285

```typescript { .api }

286

/**

287

* Helper for testing file generation

288

* @param tree - Virtual file system

289

* @param generator - Generator to test

290

* @param options - Generator options

291

* @returns Test assertions helper

292

*/

293

function testFileGeneration<T>(

294

tree: Tree,

295

generator: Generator<T>,

296

options: T

297

): {

298

toHaveCreatedFile(path: string): void;

299

toHaveUpdatedFile(path: string): void;

300

toHaveFileContent(path: string, content: string | RegExp): void;

301

};

302

303

/**

304

* Helper for testing project configuration changes

305

* @param tree - Virtual file system

306

* @param projectName - Project to test

307

* @returns Project configuration assertions

308

*/

309

function testProjectConfiguration(tree: Tree, projectName: string): {

310

toHaveTarget(targetName: string): void;

311

toHaveTag(tag: string): void;

312

toHaveProjectType(type: 'application' | 'library'): void;

313

};

314

```

315

316

**Usage Examples:**

317

318

```typescript

319

import {

320

testFileGeneration,

321

testProjectConfiguration

322

} from "@nrwl/devkit/testing";

323

324

describe('Component Generator', () => {

325

let tree: Tree;

326

327

beforeEach(() => {

328

tree = createTreeWithEmptyWorkspace();

329

});

330

331

it('should generate component files', async () => {

332

const fileTest = testFileGeneration(

333

tree,

334

componentGenerator,

335

{ name: 'my-component', project: 'my-app' }

336

);

337

338

await fileTest.toHaveCreatedFile('apps/my-app/src/app/my-component/my-component.component.ts');

339

await fileTest.toHaveCreatedFile('apps/my-app/src/app/my-component/my-component.component.html');

340

await fileTest.toHaveFileContent(

341

'apps/my-app/src/app/my-component/my-component.component.ts',

342

/export class MyComponentComponent/

343

);

344

});

345

346

it('should configure project correctly', async () => {

347

await libraryGenerator(tree, { name: 'my-lib' });

348

349

const projectTest = testProjectConfiguration(tree, 'my-lib');

350

projectTest.toHaveTarget('build');

351

projectTest.toHaveTarget('test');

352

projectTest.toHaveProjectType('library');

353

projectTest.toHaveTag('scope:shared');

354

});

355

});

356

```