I heard more and more that unit testing TypeScript is hard for two reasons. Most of the time is because of how hard is to test private method or that unit tests broken easily when modifing the code thus very expensive. Here is 3 different ways to achieve a higher number of unit test in a more atomic fashion which helps to reduce the cost of impact when modifying code. Another issue I see is we are having single public method calling several privates methods has the consequence of having multiple unit tests harder to maintain. The difficulty is that private methods need to be set in a particular state to achieve specific testing paths. Since it’s all in private methods, setting conditions to get into the desired private method require for the developer to read more code, understand many private method instead of just the one tested, arrange several parameters and finally execute the code to be tested. Not only it increases the time to create tests, since it requires a larger comprehension of the code, but make them fragile to changes. A single change in a private method could result of failing other tests when it should not.
Pattern 1 (fast, less code)
Pattern 2 – Variation 1 (separating the logic into classes and interface)
This second pattern is a better pattern than the previous one, but it comes with a cost of having more code to write. Common sense here need to be used about when to use it or not. The way to keep having public method and still being able to unit tests the code is by separating the code with a level of abstraction by dividing the logic into public classes. For example, you are creating a class that has a private method to sort a collection. Instead of having that logic into a private method, you extract the code into a SortByXYZ class which can inherit a ISortableCollectionWhatEver, then your class is injected by the concrete implementation during instantiation. The class uses that abstraction inside its methods. With that pattern, we can test the widget but also test the sorting logic because it’s in a public class — outside the class.
Pattern 2 – Variation 2 (no interface abstraction)
Imagine a class named ClassA that is having private method to render a title. Instead of having all the logic into a private method to render the title, we also created a Title class that has a public render. This way, we can test the TitleClass without having to care about ClassA implementation. This is an example about how to split logic into classes. This example does not have interface, so it’s less pluggable, but a little faster since we do not have to create interface and inject them. This is also a mix between the first pattern since you can have more public method in each class too. For example, the TitleClass could have a generateTitleFormat() method which could be public which would allow to create unit test on the formatting even if this one is only used by Render. See that solution has hardcoded instanciation of class which are as cohesive that the variation 1.
The main goal is not only to be able to unit test easily but also to be able to have cohesive classes that are easy to understand and can also be reusable. They are others pattern too available. Just keep in mind to keep it simples, easy to understand and easy to test.