TypeScript guides
...
Testing
Writing Good Tests
3 min
now that our code has been thoughtfully organized into stateless functions we can easily put them to the test with three simple steps mock run assert first, mock up data for the input parameters then, run the function with the input data to produce an output finally, assert that the output is correct test driven development ( source https //en wikipedia org/wiki/test driven development ) test driven development (tdd) is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases this is as opposed to software being developed first and test cases created later this methodology is extremely useful and productive, because it means that your code will always have test coverage not only that, but you can save time by ensuring that the feature is working simply by virtue of the tests passing, instead of having to run the entire software to see whether or not the feature or module in question is functioning correctly the following sequence is based on the book test driven development by example add a test the adding of a new feature begins by writing a test that passes if and only if the feature's specifications are met the developer can discover these specifications by asking about use cases and user stories a key benefit of test driven development is that it makes the developer focus on requirements before writing code this is in contrast with the usual practice, where unit tests are only written after code run all tests the new test should fail for expected reasons this shows that new code is actually needed for the desired feature it validates that the test harness is working correctly it rules out the possibility that the new test is flawed and will always pass write the simplest code that passes the new test inelegant or hard code is acceptable, as long as it passes the test the code will be honed anyway in step 5 no code should be added beyond the tested functionality all tests should now pass if any fail, the new code must be revised until they pass this ensures the new code meets the test requirements and does not break existing features refactor as needed, using tests after each refactor to ensure that functionality is preserved code is refactored for readability and maintainability in particular, hard coded test data should be removed running the test suite after each refactor helps ensure that no existing functionality is broken examples of refactoring moving code to where it most logically belongs removing duplicate code making names self documenting splitting methods into smaller pieces re arranging inheritance hierarchies the cycle above is repeated for each new piece of functionality tests should be small and incremental, and commits made often that way, if new code fails some tests, the programmer can simply undo or revert rather than debug excessively when using external libraries, it is important not to write tests that are so small as to effectively test merely the library itself, unless there is some reason to believe that the library is buggy or not feature rich enough to serve all the needs of the software under development antipatterns practices to avoid, or "anti patterns" having test cases depend on system state manipulated from previously executed test cases (i e , you should always start a unit test from a known and pre configured state) dependencies between test cases a test suite where test cases are dependent upon each other is brittle and complex execution order should not be presumed basic refactoring of the initial test cases or structure of the uut causes a spiral of increasingly pervasive impacts in associated tests interdependent tests interdependent tests can cause cascading false negatives a failure in an early test case breaks a later test case even if no actual fault exists in the uut, increasing defect analysis and debug efforts testing precise execution behavior timing or performance building "all knowing oracles" an oracle that inspects more than necessary is more expensive and brittle over time this very common error is dangerous because it causes a subtle but pervasive time sink across complex projects testing implementation details slow running tests