Isolation Testing12:34 with Jeremy McLain
There are many tactics for testing units independently from the rest of the code or system.
Most classes have to use other classes in some way in order to do their job. 0:04 At the beginning of this course we learned that unit 0:09 testing focuses on testing a single unit of code. 0:12 Independent from the rest of the system. 0:15 When a test fails we need to know why it failed, and that 0:18 the failure was caused by the unit being tested and not by code in some other unit. 0:21 So how do we write a unit test for a class if it depends on another class? 0:26 We should strive to test each unit independently from all of their units. 0:32 This is probably the most difficult aspect of unit testing. 0:36 There are many ways to achieve this goal. 0:41 Following good object-oriented design principles when designing software is 0:43 the first step to making this level of unit test possible. 0:48 In object-oriented design, 0:52 we strive to reduce the amount of concrete coupling there is between classes. 0:53 There are many patterns and 0:58 best practices that can help In fact, too many to cover in this course. 1:00 So, we'll leave that for another day. 1:04 However, the simplest way to reduce coupling between two classes, is to have 1:07 the client class use an interface instead of a concrete implementation of the class. 1:11 This gives us the greatest flexibility when testing the unit. 1:16 We can provide an implementation of a class that has already been unit tested, 1:20 or we can use a test double in its place. 1:25 Test doubles are also known as mocks, stubs, spies, or fakes. 1:29 Actually these are all slightly different concepts, but 1:34 as a group they're called test doubles. 1:37 You may hear people refer to them all as mocks which is probably okay. 1:40 In this course I'll refer to them generally as test doubles. 1:44 Test doubles are used to stand in for 1:48 parts of the program in order to facilitate testing. 1:50 In order to have better control over what is being tested and 1:54 to ensure that a unit is tested independently from the rest of the code. 1:57 We can replace its dependencies with much simpler objects that behave more 2:01 predictably. 2:06 To see what this really means, let's code up an example. 2:07 Treehouse Defense has both towers and invaders. 2:11 The towers try to disable the invaders by shooting at them. 2:14 Here's a basic implementation of the tower class notice that the tower class depends 2:18 on two other complex types map location and I invader. 2:23 The question is how do we test the tower class in such a way that if a test fails, 2:29 then we know that it's a failure in the tower class and 2:34 not in one of these dependencies. 2:37 One way is to unit test the dependencies first. 2:39 That's what we've done with the map location class. 2:42 However, invaders are much more complex objects than applications. 2:45 And we haven't written a concrete implementation of an invader to test. 2:48 IInvader is an interface and 2:52 we don't know what the concrete implementation of it will be yet. 2:54 However, when testing this class we need to pass it in an array of 2:57 IInvader objects. 3:01 Since we don't have a tested invader to pass it yet we can pass it a mock invader. 3:02 First let's create a test class for Tower. 3:07 I'll do this just by right clicking on Tower and clicking on Create unit test. 3:10 I'll clean this up a little bit. 3:17 Now let's create a mock invader in the test project. 3:21 I'll create this mock in its own namespace and folder. 3:24 I'll call the folder Mocks, so say add a folder. 3:27 Mocks, and in here I'll Add a new class, 3:34 and I'll call this new class 3:40 InvaderMock If we 3:44 had multiple invader mocks we'd probably want to give it a more descriptive name. 3:51 We want invaderMock to implement the I invader interface. 3:55 We can have Visual Studio do this code for 4:00 us just hit control dot on the keyboard and select implement interface. 4:03 Now that we have a concrete implementation of I invader to use, 4:08 let's go back to the tower test class and start coding the test. 4:11 We can get rid of the constructor test since it'll be tested in the course of 4:15 the other test. 4:19 And we'll remain this one to FireOnInvadersDecreases Invaders help. 4:20 So in here we'll create a new instance of a tower, 4:31 so I'll say var target = new tower. 4:36 And we need to give it a location on the map, so 4:39 we'll say new Map location and we'll just have this one be at 0, 0. 4:43 And we need to give it an instance of the map. 4:48 So let's create that say var map, 4:51 = new map make it of size 3, 3. 4:55 And pass in the map. 5:02 Now let's create our array of invaders. 5:04 So we'll say var invaders = new InvaderMock, 5:07 we need to add the namespace. 5:12 And it'll be an array of a couple of invaders. 5:19 So I'll say new invaderMock and it will need to have a location so 5:25 we'll just use a property setter for that. 5:29 So I'll say location = new map location and 5:33 we'll just have it be at 0, 0. 5:38 And let's have a couple of these. 5:45 Now we need to call fire on invaders on our target, so 5:48 let's say target.FireOnInvaders and pas in the invaders. 5:52 And our assertion will be that the health of each one of these 6:00 invaders has been decreased by one. 6:05 So to do that, we can use the Assert.All method. 6:08 So we'll say Assert.All, we'll pass in all of the invaders. 6:13 And then we give it a lambda. 6:18 So we can say, for each i in invader. 6:20 Assert that, Assert.Equal that the health is one. 6:25 So we'll say i.health. 6:33 There we go. 6:37 Now, we need to think about what we need our mock to look like, in order for 6:39 this test to work. 6:42 We can figure that out by looking at both the test, and the tower class. 6:43 So, here we see that InvanderMock is gonna need a public setter for 6:47 the location property. 6:52 And if we look at the tower class in the fire on invaders method. 6:54 We'll see that it needs to have, is active and 6:58 location and it needs to decrease the health of the invader by this much. 7:03 And that's all it needs to do in order to pass this test. 7:12 So we'll go over to our invader mock. 7:16 And just to keep this simple, we'll just have HasScored always return false. 7:19 And our Health, That's what's going to be decreased. 7:29 We'll have a getter and a private setter. 7:36 And we'll have it start out with a health of 2. 7:43 Since after calling fire on invaders, 7:46 we're going to have a health of 1 afterward. 7:49 That's what our test here is doing. 7:53 That's what it's checking. 7:55 After calling fire on invaders the health should be 1. 7:57 So we need to start off with a health of two in our mock. 8:01 And IsActive we can just have that always return true. 8:06 IsNeutralize can always return false and 8:12 our location needs to have both the getter and a setter. 8:17 So we'll say get, set. 8:23 So decrease health would just be health -= factor. 8:29 Which this is the behavior we'd expect. 8:36 Now, notice that move still is thrown knew not implemented exception. 8:40 The reason for this is in this mock we don't have to do anything with move, 8:46 because move is never called by either the test or the fire on invaders method. 8:50 So, we can just leave it as it is. 8:55 Now, if we go back to the tower test. 8:57 We'll see that we've got no red squiggly so it should compile. 8:59 And let's compile and run this test. 9:02 Let's check out the Tower Test, and it passes. 9:08 So this is just one example of using a mock to facilitate testing. 9:14 The purpose of mocks is to provide a super simplified version of a dependency 9:19 that has a very specific. 9:24 And very predictable behavior so that we can test exactly what we want to test and 9:26 no more. 9:30 Let's take a look at that Mock again, as you can see it's pretty simple. 9:31 A lot simpler than a implementation of an invader would normally be. 9:36 Mock should be as simple as possible. 9:41 Otherwise we introduce the possibility of a bug in the mock itself. 9:43 Notice that we didn't need to implement every property and method. 9:48 Only what needed to be done in order to run our test. 9:51 We could have many invader mocks that are specific 9:54 to different types of test that we want to create. 9:57 I mentioned earlier that mocks are just one category of test doubles. 10:00 There are also spies stubs and fakes. 10:04 However many people refer to all of these as mocks as well. 10:06 I've included some links in the teacher's notes if you'd like to learn more about 10:10 these other categories of test doubles. 10:14 Just as there are frameworks for unit testing, there are also frameworks for 10:15 creating doubles. 10:19 One such popular framework for .NET is called MOQ spelled M-O-Q. 10:20 You'll find a link to more information about the MOQ framework in the teacher's 10:25 notes as well. 10:28 As you can see we don't necessarily need a special framework to create MOQs. 10:29 However they can help to create better testing code. 10:34 Especially if a lot of different test doubles need to be created. 10:37 For example using a mock framework we could have written the entire fire on 10:40 invaders decreases invaders health test without actually implementing this invader 10:44 mock class. 10:49 The behavior of the invader mock class would have been put in the test itself. 10:50 Right here. 10:56 This would keep the mock code close to the test that 10:58 was using it instead of in a separate class. 11:01 While testing units in complete isolation from each other is the ideal 11:04 it isn't always practical to do so. 11:08 For example we didn't create a mock of the MapLocation class 11:11 in order to test the tower class. 11:15 In a lot of cases we can get away with this and 11:17 still maintain a high degree of confidence in our test. 11:20 The level of isolation testing to do depends on the specific situation. 11:23 There are some situations where isolation testing is unavoidable. 11:29 For example, in cases where there's uncertainty in a dependency. 11:32 And example of this is a remote service that isn't always running or 11:37 isn't completely stable. 11:40 You don't want to temporary failure in an external dependency such as this 11:42 to cause your unit test to fail. 11:46 The dependency may not respond in a timely manner. 11:49 This is another situation when creating a test double 11:52 to stand in its place is a good idea. 11:55 Another time when test doubles is unavoidable 11:58 is when the dependency isn't complete enough to rely on during testing. 12:01 In this case, we can create a simple version of the dependency, 12:05 just like we did with the invader mock. 12:09 This allows us to continue coding and 12:12 testing before all of the dependencies are complete. 12:14 Test doubles are also used to provide a reliable and predictable source of data. 12:18 A random number generator that isn't random, or 12:23 a time source that can be set to any time. 12:26 Our examples where we could use stand ins to remove 12:28 indeterminacy from a test environment. 12:31
You need to sign up for Treehouse in order to download course files.Sign up