<commit_before># Testing

Automated testing is the essential part of the software development process.
It improves the code quality and ensure that the code operates correctly especially after refactoring.

## The Different Tests

<img src="../../assets/testing-hierarchy.png" alt="Testing Hierarchy" style="float:right;width:300px;">

Our goal is to test each application layer mostly independent of others.

- **Repo tests** operate on data access layer.
  They prove that the database queries are executed correctly.
- **Use case tests** operate on logic layer.
  They verify the correct implementation of the business requirements.
- **Mapper tests** operate between logic and service layer.
  They verify the correct mapping between domain objects and objects delivered to the clients via REST API.
- **End-to-end tests** operate on all layers of the application.
  The testing endpoint is the service layer. These tests verify the availability of the expected endpoints and checks
  the expected status codes and the structure of the received objects.

## General Test Conventions

### Lean Tests

The tests should be as simple to read and understand as possible. They should be effortless to write and change, in order to not slow down development. Wherever possible:

- avoid complex logic
- cover only one case per test
- only use clearly named and widely used helper functions
- stick to blackbox testing: think about the unit from the outside, not its inner workings.
- its okay to duplicate code for each test

### Naming Convention

When a test fails, the name of the test is the first hint to the developer (or any other person) to what went wrong where. (along with the "describe" blocks the test is in).
Thus, your describe structure and testcase names should be designed to enable a person unfamiliar with the code to identify the problem as fast as possible. It should tell him:

- what component is being tested
- under what condition
- the expected outcome

To facilitate this, your tests should be wrapped in at least two describe levels.

```TypeScript
// Name of the unit under test
describe("Course Service", (() => {
    // a "when..." sentence
	describe("When a student tries to create a course", (() => {
		// a "should..." sentence
        it("should fail", async () => {
            ...
        });
    });
});
```

### Isolation

Each test should be able to run alone, as well as together with any other tests. To ensure this, it is important that the test does not depend on any preexisting data.

- Each test should generate the data it needs, and ensure that its data is deleted afterwards. (this is usually done via mocha's "afterEach" function.
- When you create objects with fields that have to be globally unique, like the account username, you must ensure the name you choose is unique. This can be done by including a timestamp.
- Never use seeddata.

### Test Structure

Your test should be structured in three seperate areas, each distinguished by at least an empty line:

- Arrange - set up your testdata
- Act - call the function you want to test
- Assert - check the result

this is known as the AAA-pattern.

## Testing Utilities

NestJS:

- provides default tooling (such as test runner that builds an isolated module/application loader)<commit_msg>add test code samples<commit_after># Testing

Automated testing is the essential part of the software development process.
It improves the code quality and ensure that the code operates correctly especially after refactoring.

## The Different Tests

<img src="../../assets/testing-hierarchy.png" alt="Testing Hierarchy" style="float:right;width:300px;">

Our goal is to test each application layer mostly independent of others.

- **Repo tests** operate on data access layer.
  They prove that the database queries are executed correctly.
- **Use case tests** operate on logic layer.
  They verify the correct implementation of the business requirements.
- **Mapper tests** operate between logic and service layer.
  They verify the correct mapping between domain objects and objects delivered to the clients via REST API.
- **End-to-end tests** operate on all layers of the application.
  The testing endpoint is the service layer. These tests verify the availability of the expected endpoints and checks
  the expected status codes and the structure of the received objects.

## General Test Conventions

### Lean Tests

The tests should be as simple to read and understand as possible. They should be effortless to write and change, in order to not slow down development. Wherever possible:

- avoid complex logic
- cover only one case per test
- only use clearly named and widely used helper functions
- stick to blackbox testing: think about the unit from the outside, not its inner workings.
- its okay to duplicate code for each test

### Naming Convention

When a test fails, the name of the test is the first hint to the developer (or any other person) to what went wrong where. (along with the "describe" blocks the test is in).
Thus, your describe structure and testcase names should be designed to enable a person unfamiliar with the code to identify the problem as fast as possible. It should tell him:

- what component is being tested
- under what condition
- the expected outcome

To facilitate this, your tests should be wrapped in at least two describe levels.

```TypeScript
// Name of the unit under test
describe("Course Service", (() => {
    // a "when..." sentence
	describe("When a student tries to create a course", (() => {
		// a "should..." sentence
        it("should fail", async () => {
            ...
        });
    });
});
```

### Handling of function return values

When assigning a value to an expect, separate the function call from the expectation to simplify debugging. This later helps when you not know about the return value type or if it's an promise or not. This is good style not only for tests.

```TypeScript
	// doSomethingCrazy : retValue
	it('bad sample', () => {
		expect(doSomethingCrazy(x,y,z)).to... 
	})
	it('good sample', () => {
		const result = doSomethingCrazy(x,y,z)
		expect(result).to... // here we can simply debug
	})

```

### Promises and tests

When using asynchronous functions and/opr promises, results must be awaited within of an async test function instead of using promise chains. While for expexting error conditions it might be helpful to use catch for extracting a value from an expected error, in every case avoid writing long promise chains. 

 - Instead of using done callback, use async test functions. 
 - Use await instead of (long) promise chains 

```TypeScript
	// doSomethingCrazy : Promise<retValue>
	it('bad async sample', async (done) => {
		return doSomethingCrazy(x,y,z).then(result=>{
			expect(result).to...
			done() // expected done
		}).catch(()=>{
			logger.info(`Could not ... ${error}`);
			done() // unexpected done, test will always succeed which is wrong
		})
	})
	it('good async sample', async () => {
		const result = await doSomethingCrazy(x,y,z)
		expect(result).to...
	})
``` 

> Timeouts must not be used, when async handling is correctly defined!

### Expecting errors in tests

When expecting an error, you might take values from an error, test for the error type thrown and must care of promises.

```TypeScript
	// doSomethingCrazy : Promise<retValue>
	it('bad async sample expecting an error', async () => {
		expect(doSomethingCrazy(x,y,z)).to...
	})
	it('good async sample expecting an error value', async () => {
		const code = await doSomethingCrazy(x,y,z).catch(err => err.code)
		expect(code).to...
	})
	it('good sample expecting an error type', async () => {
		expect(() => doSomethingCrazySync(wrong, param)).toThrow(BadRequestException);
	})
	it('good sample expecting an error type from an async function', async () => {
		await expect(() => doSomethingCrazySync(wrong, param)).toThrow(BadRequestException);
	})
```

### Isolation

Each test should be able to run alone, as well as together with any other tests. To ensure this, it is important that the test does not depend on any preexisting data.

- Each test should generate the data it needs, and ensure that its data is deleted afterwards. (this is usually done via mocha's "afterEach" function.
- When you create objects with fields that have to be globally unique, like the account username, you must ensure the name you choose is unique. This can be done by including a timestamp.
- Never use seeddata.

### Test Structure

Your test should be structured in three seperate areas, each distinguished by at least an empty line:

- Arrange - set up your testdata
- Act - call the function you want to test
- Assert - check the result

this is known as the AAA-pattern.

## Testing Utilities

NestJS:

- provides default tooling (such as test runner that builds an isolated module/application loader)
{'fraction_non_alphanumeric': 0.07840486650895573, 'fraction_numerical': 0.0006759040216289286, 'mean_word_length': 3.8968560397131826, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2102181', 'n_tokens_mistral': 2267, 'n_tokens_neox': 2176, 'n_words': 1232}