Content 1. Definition of Unit Test and benefits of writing unit tests. 2. TDD introduction, methodology and benefits. 3. Effective use of TDD. 4. Applying TDD to our project.
What is a “Unit Test”? A "unit" is a piece of code, method or function that has a single responsibility. A "unit test" is a piece of code that invokes another piece of code and checks the correctness of some assumptions. If the assumptions turn out to be wrong, the unit test has failed.
Unit Test Benefits compare to Integration Test Integration Test Unit Test• A integration test group 2 or more • A unit test tests only a single unit in dependent units and test to see if they isolation work properly together.• Integration tests are harder to write, • Unit tests are easy to implement, they easy to fail if they have an external works with fake dependencies(Stub and dependency which is unavailable for Mock), run and give results quickly some reason• Integration tests are hard to maintain. • Unit tests are super easy to maintain When a test fail, we have to debug to because they cover a small piece of see what is our problem. functionality.• Integration tests help to document • Unit tests help to design and document main features of our application. our code.
TDD Introduction Traditional approach: many people feel that the best time to write unit tests for software is after the software has been written. It sounds reasonable but there is code we can’t write unit test without modifying. There’re many cases we have to modify our code to make it unit-testable, which may lead to other problems, slow down our schedule… Test Driven Development: Write tests first, use unit tests drive the design and document our code. With TDD, we begin with the end in mind.
TDD Methodology The general TDD cycle goes as follows: 1. Write a failing test first to prove code or functionality is missing. The test will fail because we havent implemented that functionality yet. 2. Make the test pass by writing production code that meets the expectations of your test. At that moment, we just need to pass the test, the code should be as simple as possible. 3. Refactor your code when the test passes, make it more readable, remove code duplication... By writing and passing unit tests, we add more functionality, a bit at a time until all requirements are fulfilled.
TDD Benefits Writing unit tests actively will make most of our code are covered by unit tests. That will raise the confidence in the code. Help to create quality code, better designs, document our code....Write test first help us really understand the design of code. Instead of writing code to do something, were starting by outlining all conditions we may have, all expected output. Speed up our development in a long run. We done when we finish the last line of code, we dont need to spend much time debugging, we dont need to manually check if our new features break the old code, existing features.
Effective use of TDDTDD brings us a lot of benefits. TDD = TooHowever, there is no silver bullet, TDD Damndoesn’t ensure project success. On the Difficultother hand, TDD is a double-edgessword and if it is done incorrectly, itwill waste our time, lower our codequality.In order to apply TDD effectively, wehave to know exactly what to test andhow to write a good unit test.
What to test? Any classes and components that contain logical code, e.g. if…else, for, switch…case, any calculation or interaction with other components, calls to external methods. Logical code could be found on server side or client size. We have to test them all.
Unit testing on server side Repository Layer: verify the persistence and retrieval implementations produce the correct results. Business Logic Layer: focus on verifying the business rules and their interactions with Repository Layer. The tests do not actually store data in the repository but use a fake repository and verify the business services layers uses it correctly. Web Layer: we create unit test for request handlers, verify their interactions with business service, e.g., "Are they called correctly?", "Are the view models returned correctly?"
Unit testing on client side The basic rule is to test anything a designer would not change. We would not test that a specific font value was set or a specific background color of an element was used. We test the property values of a view model, whether a calculation is correct, an event is triggered, a DOM element are modified correctly, a AJAX callback function has been called or not...
How to write a good unit test? A good unit test must be simple, trustworthy, maintainable and readable. Naming unit test: MethodUnderTest_Scenario_ExpectedBehavior() ex: AuthorizeUser_WrongUserName_ReturnFalse() Avoid logic in tests: having logic will increase chances of having bugs in unit tests. avoid "if, switch, for..." to generate input, try to hard code. Testing only one thing, avoid testing multiple aspects: each unit test should generally take one action and make one assertion. if our tests contain more than a single assert and the first assert fails, it will throw exception and the second assert never run.
How to write a good unit test? Avoid duplication: put duplicated code in TestCleanup and TestInitialize method for maintainable reason. Dont test things that obviously work. Enforce test isolation: a test should always run in its little world, isolated from its dependencies. We do not want to think our subject under test is broken because one of its dependencies broke and caused the subject under test to fail. If We test like this, We might spend a lot of time tracking down bugs in the wrong place. Testing how things operate together is the domain of integration testing, not unit testing.
Applying TDD to our project Server side: We still have not implemented business services yet, so, most of code reside in action’s Execute methods. In those methods, we don’t care about what business services do or what they return, we only need to test, if we have to, whether they have been called correctly or not.
Applying TDD to our project On client side, we have 3 layers that contain logic: View Model: verify all behaviors(CopyUser, LoadUser…), computed properties Repository: verify whether the call to server has been made and callback function has been triggered correctly or not. Presenter: most of our UI logic resides here. We should write unit test to verify circumstances, e.g. whether a button is enabled/disabled, a list is empty, selected item changed, user click Save button.
How to write unit-testable code?Just need to remember: “objects do not create other objects onwhich they rely to do their work. Instead, they get the objects thatthey need from an outside source”.We have 4 common ways to achieve this: Constructor, Setter andGetter, Interface, Service Locator(factory pattern, local factorymethod).All necessary techniques can be found in: The Art of Unit Testing – Manning Test-driven Development By Example - Addison Wesley Dependency Injection in .NET - Manning