TDD in .NET in Hebrew
22 lessons 7 h 44 min 5 sections Spoken in Hebrew
Start course Watch on YouTube playlist (external link, opens in new tab)
Recorded live during a 3 day course in Tel Aviv, Israel
About this course
Over three days, Roy Osherove teaches unit testing (external link, opens in new tab) and test-driven development (external link, opens in new tab) in .NET with NUnit (external link, opens in new tab), starting from the definition of a unit of work (external link, opens in new tab) and a calculator built test first. The second day covers handwritten fakes (external link, opens in new tab), mocks versus stubs (external link, opens in new tab), isolation frameworks (external link, opens in new tab) from Moq (external link, opens in new tab) and NSubstitute (external link, opens in new tab) to Typemock Isolator (external link, opens in new tab), and extract and override (external link, opens in new tab) for static dependencies. The third day turns his practices for readable, trustworthy (external link, opens in new tab) and maintainable tests (external link, opens in new tab) into reviews of open-source tests and applies refactorings (external link, opens in new tab) such as instance delegator to the legacy BlogEngine.NET project.
Curriculum
Section 1
Day1 - Unit Testing and TDD 101
The first day defines a unit test as a check of a unit of work, from a public entry point to a return value, state change or third-party call, and explains why test suites fail when they are untrustworthy, hard to maintain or unreadable. Roy Osherove introduces the xUnit frameworks and NUnit, test naming and structure, and builds a calculator with tests in Visual Studio with ReSharper, NuGet and TestDriven.NET, covering TestCase, exception asserts and continuous test runners. The day ends with a calculator memory feature written test first and a preview of mocks and stubs.
- Introduction Duration 33:07
Roy Osherove lays out the three days (unit testing basics, mock objects, legacy code) and separates unit testing, how to write tests, from TDD, when to write them. He asks why teams with many tests still give up on them: tests that check nothing, tests that cost too much to maintain, and unreadable test names. He defines a unit as a unit of work that starts at a public method and ends in a return value or exception, a state change, or a call to a third party such as a logger.
- Syntax Duration 29:43
Roy Osherove argues that unit tests and integration tests complement each other, then introduces the xUnit family of frameworks, from SUnit through JUnit to NUnit, which the course uses, and its competitor MSTest. He explains the arrange, act and assert parts of a test, the TestFixture and Test attributes the runner finds through reflection, a three-part test name separated by underscores, putting the expected value before the actual one, and one logical assert per test. He starts a calculator class library in Visual Studio with ReSharper, adds a separate test project and installs NUnit with NuGet.
- Test Demo Duration 34:54
Roy Osherove shows where NuGet comes from and writes the first calculator test with ReSharper, explaining why he picks plain inputs such as 1 and 2 over numbers like 1337 that make a reader wonder. He prefers the TestDriven.NET runner, maps a keyboard shortcut to rerun the last test action, and runs any method under the debugger without a test. He adds a test for negative numbers, keeps random values and logic out of unit tests, and replaces the SetUp method with a factory method so each test stays readable on its own.
- Test Cases, String Asserts and more Duration 34:49
Roy Osherove merges similar tests with NUnit's TestCase attribute and compares ways to test an exception: try/catch with Assert.Fail, the ExpectedException attribute, which can pass when another line throws, and Assert.Throws with a lambda. He treats a TearDown as a sign of an integration test, to be kept in a separate project, and names the continuous test tools AutoTest.NET, NCrunch and Mighty Moose. He then introduces the three TDD steps and starts a calculator memory feature test first.
- TDD Demo Duration 19:26
Roy Osherove builds the calculator's memory feature with TDD: first a failing test, then the simplest code that passes all tests, which he describes as code with as many assumptions as possible, near the top of the function and with few ifs. The group designs the API through the tests, and he writes future tests down as comments so there is one problem to solve at a time. He also answers questions on organizing many tests, given/when/then in behavior driven development, and setup methods.
- Summary Day 1 Duration 8:45
Roy Osherove notes that by the end of the first day most participants were writing the test first and arguing about implementation, design and readability rather than whether to test at all. He previews the second day: replacing the console earlier was a kind of mock object, the difference between a mock (which the test asserts against, one per test) and a stub (any number per test), and frameworks such as NSubstitute. He asks everyone to redo the string calculator exercise from scratch for ten to fifteen minutes every morning so they do not forget what they learned.
Section 2
Day 2 - Mock Objects and TDD
The second day opens with participants' questions about UI testing, storage and asynchronous code, then moves to fakes: a handwritten fake logger and web service passed in through the constructor, and the difference between a mock, which the test asserts against, and a stub. Roy Osherove compares constrained isolation frameworks such as Rhino Mocks, Moq, FakeItEasy and NSubstitute with profiler-based ones such as Typemock Isolator, rewrites the same tests with several of them, and shows recursive fakes.
- TDD Q & A Duration 35:14
The second day opens with participants' questions. Roy Osherove suggests adding a command-line step that runs the tests in the build and fails it, and on UI testing contrasts recorded WinForms tests with Ruby's Capybara, which can run headless and test JavaScript, while recommending page logic such as validation be extracted into classes that unit tests cover. He keeps DateTime.Now, random numbers and threads out of unit tests, gives three ways to test code that ends in storage or a request (abstraction, a fake, or an integration test), and advises using mock objects as little as possible.
- Mocks and Stubs Duration 24:52
Roy Osherove explains why a test replaces a dependency, using a manager class whose save method calls a logger that may be unfinished, buggy or slow. He adds a logger interface passed through the constructor and a handwritten fake logger in the test file; a fake is a mock when the test asserts on it and a stub otherwise, as in a second test where a stub logger throws and a fake web service is asserted. He advises one mock per test and no logic in fakes or tests, and shows NCrunch running tests in the background.
- Isolation Frameworks Duration 34:31
Roy Osherove explains that isolation frameworks generate fakes in memory that return values, throw exceptions and verify calls, and that any fake is a mock if the test verifies against it and a stub otherwise. He splits .NET frameworks into constrained ones such as Rhino Mocks, Moq, FakeItEasy and NSubstitute, which can only fake virtual or interface methods, and profiler-based ones such as Typemock Isolator, JustMock and Moles, which can also fake static methods. He then rewrites the logger tests with NUnit's basic dynamic mocks, Rhino Mocks and Moq, comparing string method names with lambda expressions, strict and non-strict mocks.
- Advanced Mocks and Stubs Duration 12:50
Roy Osherove continues with Moq, showing callbacks, Verifiable and VerifyAll, and overriding protected methods, and warns that verifying many interactions you do not care about is overspecification, explained with a story about sending his son to buy pizza. He then writes the same logger tests with FakeItEasy, whose API starts with A and whose error messages he finds better than Moq's, and with NSubstitute, and recommends starting with one of the newer frameworks. Asked about profiler-based frameworks, he says they run slower but are used to get coverage on legacy code before refactoring.
- Recursive Fakes Duration 3:10
With NSubstitute, Roy Osherove shows recursive fakes: when a person object is faked, its manager property returns a fake as well, instead of each returned object being set up on its own line. He says Isolator was the first framework to do this and that it makes tests more maintainable, because a new property or method that returns an object is faked by default. He then sends the group back to the exercises, up to the login manager that uses a static method.
Section 3
Day 2 - Legacy Code Tools
This section deals with dependencies that cannot be passed in through a constructor: extract and override for a static logger and the machine name, a settable system time class for the current time, and Typemock Isolator for faking static methods, future instances and non-public members without changing production code. It ends with a preview of the third day's legacy project.
- Legacy Refactoring intro Duration 16:23
Roy Osherove turns to dependencies that cannot be injected through a constructor, such as a call to a static logger, and introduces extract and override from Michael Feathers' book Working Effectively with Legacy Code, which he recommends. He moves the call into a protected virtual method with no logic and has a testable subclass of the login manager override it and save the text in a public field for the test to check, noting that in real legacy code he would first have an integration test in place. To concerns about exposing such methods he answers with internal methods and InternalsVisibleTo.
- Controlling Time Duration 6:44
Roy Osherove applies extract and override to the machine name as well, then explains why it does not suit time, which is used throughout a system. Instead he adds an API to a central system time class so tests can set the current time, and resets it after each test because the change is static and affects other tests. He argues that a static class is not bad design in itself, suggests making the setter internal and visible to the test assembly, and sends the group to implement it in the exercises.
- Working with Typemock Isolator Duration 26:41
Roy Osherove shows Typemock Isolator, a profiler-based framework for code that cannot be refactored: its API starts with Isolate, it can fake any class, including ones with private constructors, and it verifies calls with any or exact arguments. He explains the Isolated attribute that cleans up static fakes after each test and the profiler environment variables that Visual Studio plugins and command-line runners set, as Moles and JustMock also need. He then fakes static methods, future instances and non-public members without touching production code, mentions SilverUnit and CThru, which he built on Isolator, and asks the group to test the original exercise code this way.
- Day 2 Summary Duration 2:23
Roy Osherove previews the third day: the group will take an open-source project that has no tests and is mostly static and write tests for it both without and with Isolator, learning another refactoring technique on the way. They will also turn the practices discussed so far into a more formal set of things to look for, review tests from open-source projects, and, if time allows, write tests for the participants' own code.
Section 4
Day 3 - Unit Testing Practices
Roy Osherove formalizes his rules for tests that are readable, trustworthy and maintainable: keep unit and integration tests apart, review tests first, and avoid logic, magic numbers, dependencies between tests and overspecification. He applies them in reviews of real tests from Microsoft's Unity, ASP.NET MVC and NerdDinner, and closes with ordering tests so that each changes only one input.
- Unit Testing Best Practices Duration 35:00
Roy Osherove groups his rules into tests that are readable, trustworthy and maintainable, and tells how a bug in a test helper cost him and a pairing partner 90 minutes on a Go engine built with TDD. For trust he separates unit tests from integration tests that need configuration, so a failing unit test is never "fine", and recommends code reviews that start from the tests. He argues that code coverage means little without reviews, then reviews a test from Microsoft's Unity source that has no expected behavior in its name, a loop and several asserts, and prefers readability to removing duplication.
- Best Practices Continued Duration 25:20
Roy Osherove advises against tests that depend on each other's order and against magic numbers such as a 37 in the Unity tests, preferring the simplest value or a named variable. He shows an ASP.NET MVC test helper that builds a fake HTTP context with Moq and is full of ifs, suggests overloads instead, and wants expected values hard-coded rather than computed, so a production bug is not repeated in the test. Reviewing a NerdDinner test named "should be valid when all properties correct", the group debates splitting it into one test per validation rule.
- Maintainability Practices Duration 34:43
Roy Osherove ties maintainability to a unit of work that runs from a real entry point to an end result, calling checks on how things are done overspecification, and wants isolated tests that can run in any order. He reviews an ASP.NET MVC test that loops over link overloads, which hides which overload failed, and a Moq test with two verifies where the data provider should be a stub. On readability he covers project conventions, a NerdDinner test with an unexplained 6, and a fake membership provider whose secret input protocol belongs in public constants.
- Readability Practices Duration 1:23
Roy Osherove recommends ordering tests so that each one changes only one thing in the inputs compared with the test before it, such as one more character in a string, which makes the pattern readable and shows how the feature was built step by step. He sums up his three pillars, tests that are easy to read, easy to maintain and trustworthy, and calls TDD almost the only way he knows to see tests both fail and pass, though bugs in tests can still mislead.
Section 5
Day 3 - Legacy Code
The last section works on BlogEngine.NET, a blog engine with acceptance tests but no unit tests: the group first gets its tests to pass, then makes a static manager class that sends pings and trackbacks testable with the instance delegator refactoring. The closing discussion covers how much code to include in a unit of work, when to fake, and testing private methods, and each pair writes tests for real production code.
- Legacy BlogEngine Project Overview Duration 8:02
Roy Osherove introduces the legacy project for the third day, the BlogEngine.NET blog engine, which has no unit tests but has integration and acceptance tests. Some are written with WatiN, which is based on Ruby's Watir and works mainly with Internet Explorer, and others in a framework whose tests are written in Razor pages that run in the browser; of 20 tests, five fail. The exercise is to understand why and make all the tests pass, get to know the code and its dependencies, and try writing a new test before refactoring.
- Refactoring- Introduce Instance Delegator Duration 17:36
Roy Osherove sets out to test the static Send method of a class named Manager in the ping namespace, which sends a trackback or ping for the links in a post or comment, and suggests finding its dependencies by calling it and seeing where it fails. The calls that send a trackback or ping are the end results, and the tests should prove the loop and each if. Because the class is static, he introduces instance delegator: remove static from the class, move the logic into an instance method that the static method calls on a new manager, then extract and override settings such as the trackback check in a testable subclass.
- Summary and Discussion Day 3 Duration 18:48
Roy Osherove explains that a fixture setup runs once before all tests and usually means something slow is initialized, and agrees that the refactorings made the code more testable, not better designed. On whether to fake the method that parses the links out of the content, he prefers to keep as much code in the unit of work as stays fast, in memory and readable, fakes threads, processes and the file system, and calls the choice the art of unit testing; a debate on testing private methods follows. Each pair then writes two or three tests for real production code that go into source control.