With TDD, you write the test before you write the code that passes the test. It's a strange way of thinking about things and one I find bloody difficult.
This by Uncle Bob Martin really goes through it well.
I recently asked my prof the same thing, and he recommended this: https://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445
A little older but apparently still very good. Haven't gotten to it yet tho so I can't really tell you more.
Robert C. Martin, a.k.a. Uncle Bob, initially wrote a book for Java (2002) before the C# (2006) book was written. The only version he seems to maintain, or at least I can find links for on GitHub, is Java.
The book has helped me understand concepts, and most consider Uncle Bob one of the essential authors of computer programming. The C# book has code throughout, especially Section 4, but I cannot find a downloadable source. You'd have to input by hand, page-by-page if you wanted the complete system.
> But no one explains what it's supposed to mean. How do we know what a "responsibility" is? A feature that's a "single responsibility" in my mind might very well be composed of 10 responsibilities in someone else's mind.
"What Is a Responsibility? In the context of the SRP, we define a responsibility to be “a reason for change.” If you can think of more than one motive for changing a class, then that class has more than one responsibility. This is sometimes hard to see. We are accustomed to thinking of responsibility in groups. For example, consider the Modem interface in Listing 8-1. Most of us will agree that this interface looks perfectly reasonable. The four functions it declares are certainly functions belonging to a modem.
Listing 8-1 Modem.java -- SRP Violation interface Modem { public void dial(String pno); public void hangup(); public void send(char c); public char recv(); }
However, there are two responsibilities being shown here. The first responsibility is connection manage- ment. The second is data communication. The dial and hangup functions manage the connection of the modem, while the send and recv functions communicate data. Should these two responsibilities be separated? That depends on how the application is changing. If the application changes in ways that affect the signature of the connection functions, then the design will smell of Rigidity because the classes that call send and recv will have to be recompiled and redeployed more often than we like. In that case the two responsibilities should be separated as shown in Figure 8-3. This keeps the client applica- tions from coupling the two responsibilities.
If, on the other hand, the application is not changing in ways that cause the the two responsibilities to change at different times, then there is no need to separate them. Indeed, separating them would smell of Needless Complexity . There is a corollary here. An axis of change is an axis of change only if the changes actually occur. It is not wise to apply the SRP, or any other principle for that matter, if there is no symptom."