Complete Guide to Testing in NX: Building Reliable Angular Applications
Dhruv Patel
"MEAN Stack Developer | Full-Stack Web Applications | Specialist in E-commerce, HRMS & Real-time Systems"
Hello developers! ?? This week, we're diving deep into testing strategies for your NX workspace. Let's explore how to build robust, reliable applications with comprehensive testing practices.
?? Unit Testing in NX
Setting Up Your First Test
// libs/shared/ui/src/lib/button/button.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ButtonComponent } from './button.component';
describe('ButtonComponent', () => {
let component: ButtonComponent;
let fixture: ComponentFixture<ButtonComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ButtonComponent]
}).compileComponents();
fixture = TestBed.createComponent(ButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should emit click event', () => {
// Arrange
const spy = jest.spyOn(component.clicked, 'emit');
// Act
component.onClick();
// Assert
expect(spy).toHaveBeenCalled();
});
});
Running Unit Tests
# Run tests for a specific project
nx test my-app
# Run tests with coverage
nx test my-app --coverage
# Watch mode for development
nx test my-app --watch
?? E2E Testing with Cypress
Basic Configuration
// apps/my-app-e2e/cypress.config.ts
import { defineConfig } from 'cypress';
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
export default defineConfig({
e2e: {
...nxE2EPreset(__dirname),
baseUrl: 'https://localhost:4200',
specPattern: './src/e2e/**/*.cy.{js,jsx,ts,tsx}',
supportFile: './src/support/e2e.ts'
}
});
Writing E2E Tests
// apps/my-app-e2e/src/e2e/login.cy.ts
describe('Login Flow', () => {
beforeEach(() => {
cy.visit('/login');
});
it('should successfully log in', () => {
cy.get('[data-testid="username"]').type('testuser');
cy.get('[data-testid="password"]').type('password123');
cy.get('[data-testid="login-button"]').click();
cy.url().should('include', '/dashboard');
});
it('should show error for invalid credentials', () => {
cy.get('[data-testid="username"]').type('wrong');
cy.get('[data-testid="password"]').type('wrong');
cy.get('[data-testid="login-button"]').click();
cy.get('[data-testid="error-message"]')
.should('be.visible')
.and('contain', 'Invalid credentials');
});
});
?? Best Testing Practices
1. Test Data Management
// libs/shared/testing/src/lib/test-data.ts
export const mockUser = {
id: 1,
name: 'Test User',
email: '[email protected]'
};
export const mockProducts = [
{ id: 1, name: 'Product 1', price: 99.99 },
{ id: 2, name: 'Product 2', price: 149.99 }
];
2. Custom Test Utilities
// libs/shared/testing/src/lib/test-utils.ts
export function createMockStore() {
return {
select: jest.fn(),
dispatch: jest.fn()
};
}
export function mockHttpResponse<T>(data: T) {
return of(data);
}
3. Component Testing Strategy
describe('ComplexComponent', () => {
// Arrange
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ComplexComponent],
providers: [
{ provide: UserService, useValue: mockUserService },
{ provide: Store, useValue: createMockStore() }
]
});
});
// Test different states
it('should show loading state', () => {});
it('should show error state', () => {});
it('should show success state', () => {});
});
?? Test Coverage and Reporting
Coverage Configuration
{
"test": {
"options": {
"coverage": {
"reporter": ["text", "html"],
"exclude": ["**/test-setup.ts"],
"thresholds": {
"global": {
"statements": 80,
"branches": 80,
"functions": 80,
"lines": 80
}
}
}
}
}
}
?? CI/CD Integration
GitHub Actions Example
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: nx run-many --target=test --all
- name: Run e2e tests
run: nx run-many --target=e2e --all
?? Pro Testing Tips
<button data-testid="submit-button">
Submit
</button>
2. solate Network Requests
// Cypress example
cy.intercept('GET', '/api/users', { fixture: 'users.json' });
3. Group Related Tests
describe('UserComponent', () => {
describe('initialization', () => {
// Setup tests
});
describe('user interactions', () => {
// Interaction tests
});
describe('error handling', () => {
// Error tests
});
});
?? Common Testing Patterns
1. Component Testing
it('should render user details', () => {
// Arrange
component.user = mockUser;
// Act
fixture.detectChanges();
// Assert
const nameElement = fixture.debugElement.query(
By.css('[data-testid="user-name"]')
);
expect(nameElement.nativeElement.textContent)
.toContain(mockUser.name);
});
2. Service Testing
it('should fetch user data', (done) => {
const service = TestBed.inject(UserService);
service.getUser(1).subscribe(user => {
expect(user).toEqual(mockUser);
done();
});
});
#Angular #NX #Testing #WebDevelopment #QualityAssurance #Frontend #JavaScript #TypeScript #Cypress #Jest
?? Remember: Tests are an investment in your application's future. They help you catch bugs early and refactor with confidence!
Need help implementing these testing strategies in your NX workspace? Drop a comment below! ??