L’écosystème frontend évolue fréquemment. Les paradigmes changent (templating côté
serveur, spa…), de nouveaux frameworks sortent (Angular, React, Vue
JS…), de nouvelles librairies continuent d’apparaître…
À ce jour, aucun des grands frameworks javascript n’a réussi à s’imposer
comme standard de l’environnement frontend. Aussi, le code source d’une application
Web peut fortement varier d’un projet à l’autre selon la stack choisie.
Qu’en est-il des tests ? Chaque framework propose au moins une librairie
pour tester le rendu de ses composants. Nous pouvons citer Enzyme pour
React, la solution native pour Angular, etc. Toutefois, ces tests
souffrent souvent du même défaut, à savoir que leur code source est très
lié au framework javascript utilisé.
Plusieurs frameworks, plusieurs manières d’écrire un composant
Exemple avec Angular
@Component({
selector: 'todo-list',
template: `
<ul>
<li *ngFor="let todo of todos">{{todo}}</li>
</ul>
<input #newTodo/>
<button (click)="addTodo(newTodo.value)">Add</button>
`
})
export class TodoListComponent {
todos = [];
addTodo(newTodo) {
this.todos.push(newTodo);
}
}
Exemple avec React
export default function TodoList() {
const [todos, setTodos] = useState([]);
const todoInput = useRef(null);
const addTodo = newTodo => setTodos([...todos, newTodo]);
return <>
<ul>
{todos.map((todo, i) => <li key={i}>{todo}</li>)}
</ul>
<input ref={todoInput}/>
<button onClick={() => addTodo(todoInput.current.value)}>Add</button>
</>;
}
Les deux codes sources sont très
différents, bien que le résultat affiché dans le navigateur soit
strictement identique.
Plusieurs frameworks, plusieurs manières de tester un composant
Exemple avec Angular
describe('TodoListComponent', () => {
it('should add two todos', () => {
// Arrange
TestBed.configureTestingModule({declarations: [TodoListComponent]});
const fixture = TestBed.createComponent(TodoListComponent);
const component = fixture.componentInstance;
expect(component).toBeDefined();
const hostElement = fixture.nativeElement;
const button = hostElement.querySelector('button')
const input = hostElement.querySelector('input')!;
// Act
input.value = 'First';
input.dispatchEvent(new Event('change'));
button.dispatchEvent(new Event('click'));
input.value = 'Second';
input.dispatchEvent(new Event('change'));
button.dispatchEvent(new Event('click'));
fixture.detectChanges();
//Assert
expect(hostElement.querySelectorAll('li').item(0)!.textContent).toEqual('First');
expect(hostElement.querySelectorAll('li').item(1)!.textContent).toEqual('Second');
});
});
Exemple avec Enzyme pour React
describe('TodoList', () => {
test('should add two todos', () => {
// Arrange
const wrapper = mount(<TodoList/>);
// Act
wrapper.find('input').instance().value = 'First';
wrapper.find('button').simulate('click');
wrapper.find('input').instance().value = 'Second';
wrapper.find('button').simulate('click');
//Assert
expect(wrapper.find('li')).toHaveLength(2);
expect(wrapper.find('li').at(0).text()).toBe('First');
expect(wrapper.find('li').at(1).text()).toBe('Second');
});
});
Les scénarios utilisateurs sont identiques, mais l’écriture des tests varie fortement d’un framework à l’autre.