# Clean Code - 9 Unit Tests
Writing automated unit tests is encouraged because of the trend of Agile and TDD movements. However, it is important to **write good tests instead of just writing tests**.
## The Three Laws of TDD
Other resources:
https://blog.cleancoder.com/uncle-bob/2014/12/17/TheCyclesOfTDD.html
https://www.ibm.com/garage/method/practices/code/practice_test_driven_development/
> The tests and the production code are written together, with the tests just a few seconds ahead of the production code.
>
>
> ### First Law
> You may not write production code until you have written a failing unit test.
>
> ### Second Law
> You may not write more of a unit test than is sufficient to fail, and not compiling is failing.
>
> ### Third Law
> You may not write more production code than is sufficient to pass the currently failing test.
#### Example
**Phase 1**
Write a first failing unit test:
```
// production codes
// first method
function getNumbersFromStringKey(sourceStr: string) : number {
}
// below are unit tests
function testGetNumbersFromStringKeyWithNumberStr() : void {
const result = getNumbersFromStringKey('010789666');
expect(!Number.isNaN(result)).toEqual(true);
}
```
**Phase 2**
Since the unit test `testGetNumbersFromStringKeyWithSpaces` will fail, should modify the production codes to pass the test case, instead of creating more test cases:
```
// production codes
// first method
function getNumbersFromStringKey(sourceStr: string) : number {
return +sourceStr;
}
// below are unit tests
function testGetNumbersFromStringKeyWithNumberStr() : void {
const result = getNumbersFromStringKey('010789666');
expect( !Number.isNaN(result) ).toEqual(true);
}
function testGetNumbersFromStringKeyWithSpaces() : void {
const result = getNumbersFromStringKey('010 789 666');
expect( !Number.isNaN(result) ).toEqual(true);
}
```
**Phase 3**
Can start to write another production method after all unit tests pass:
```
// production codes
// first method
function getNumbersFromStringKey(sourceStr: string) : number {
const result = +sourceStr.replace(/\D/g, "");
return result;
}
// below are unit tests
function testGetNumbersFromStringKeyWithNumberStr() : void {
const result = getNumbersFromStringKey('010789666');
expect( !Number.isNaN(result) ).toEqual(true);
}
function testGetNumbersFromStringKeyWithSpaces() : void {
const result = getNumbersFromStringKey('010 789 666');
expect( !Number.isNaN(result) ).toEqual(true);
}
function testGetNumbersFromStringKeyWithNotNumber() : void {
const result = getNumbersFromStringKey('010*789++666');
expect( !Number.isNaN(result) ).toEqual(true);
}
```
## Keeping Tests Clean
When tests are dirty, it's almost the same as no tests. Discipline is:
> Test code is just as important as production code.
#### How dirty tests affect production codes
- Scenario 1: When updating the function body of the production codes (still the same API, only change the body of a function)
- The dirty tests will not fail since the goal of functions does not change
- Scenario 2: When updating the entire API (for example, new functions take more parameters, or some functions no longer exist)
- The dirty tests will fail and it takes lots of effort to fix them to pass the new functions

### Tests Enable the -ilities
Keeping unit tests clean helps keep production code flexible and makes changing it easier and more confident.
## Clean Tests
The clean test is all about readability. Readability includes:
- Clarity
- Simplicity
- Density of expression: eg, if, while, should not have too many, should not be too complicated
#### Bad unit tests example in Jave

#### Same unit test after refactor
Use the BUILD-OPERATE-CHECK pattern
http://fitnesse.org/FitNesse.FullReferenceGuide.UserGuide.WritingAcceptanceTests.AcceptanceTestPatterns.BuildOperateCheck

### Domain-Specific Testing Language
Write your own API for test use, and you need to constantly update and refactor according to test needs.
#### Example
**Before having APIs:**

**After writing and using the APIs:**

### A Dual Standard
While APIs for testing needs to be as simple and clean as production code, they **don't need to be as efficient as production code**. The reason is that the tests are run in a test environment, which has different requirements than the production environment.
#### Example
**Need to move eyes back and forth to read the test:**

**Increase the readability:**

**How the `getState` method is implemented:**

## One Assert per Test
Sometimes the **same code can appear in multiple tests** when there is only one assert in each test:

#### Template Method
To solve the above duplication of code problem, can use the Template Method.
Source https://www.artima.com/weblogs/viewpost.jsp?thread=35578
Use **Given, When, Then**:
- Given
- preconditions, things should be done **before tests**, and be written as a function
- When
- actions
- Then
- verification
Example in pseudocodes:
```
Scenario: test sign-in user checkout
Given: create an account and sign in
When: put a product in the cart and checkout
Then: the order summary has the user and product information
```
### Single Concept per Test
Test a single concept in each test function. When there are multiple concepts in a test:

**The scenarios and preconditions in the above test:**
> Given the last day of a month with 31 days (like May):
**Scenario 1:**
Add 1 month, the last day of the new month should be 30th
**Scenario 2:**
Add 2 months, the last day of the new month should be 31st
> Given the last day of a month with 30 days in it (like June):
**Scenario 3:**
Add 1 month, the last day of the new month should be 30th
The above test should be divided into 3 tests. Should also include other missing tests like **the last day of a month with 28 days in it (like February)**
## F.I.R.S.T
### Fast
> Tests should be fast. They should run quickly.
### Independent
> Tests should not depend on each other.
### Repeatable
> Tests should be repeatable in any environment.
For example, tests should be able to run on both the production environment and the OA environment.
### Self-Validating
> The tests should have a **boolean output**. Either they pass or fail.
### Timely
> The tests need to be written in a timely fashion. Unit tests should be written just **before** the production code that makes them pass.
## Conclusion
We should always keep our tests clean.
###### tags: `learn` `clean code` `test automation`