Lesson 3 of 14
Test Driving a Console Application Using String Calculator
In this talk, Roy adds a new layer to the application, and uses the existing string calculator (external link, opens in new tab), integrated and used from inside a console application.
It is also the first time the idea of a mock object (external link, opens in new tab) is introduced.
In this lesson
Roy Osherove test-drives a console application that uses the string calculator (external link, opens in new tab) by calling its Main method in memory from a unit test (external link, opens in new tab), after making Program and Main public. He mentions compiler flags and the InternalsVisibleTo attribute (external link, opens in new tab) for keeping members hidden, then redirects console output with Console.SetOut to a StringWriter over a StringBuilder and checks it with StringAssert. The aim is to connect the string calculator layer to the console layer with a couple of tests, and he moves the console replacement into a helper method (external link, opens in new tab).
Transcript
[00:26] (external link, opens in new tab) Yesterday we and now we you all have a a very basic version of the string calculator (external link, opens in new tab). What we're going to do now is we're going to try to do to attempt to create a console application that uses the string calculator. Okay. So we're going to try and do it with a TDD (external link, opens in new tab). We're going to try to do a test first. But first, I want to explain how how my design cho choices are going to work. So to understand how to test first a console application, let's see what a console application looks like.
[01:00] (external link, opens in new tab) So if I go to my solution and I create add new project console application, I'm going to call it string master cuz I want to be able to say string master.exe. What is the structure of the string master? Well, it has a program class. It's not public. It has a main method, which is the main entry point. Let's get rid of this.
[01:31] (external link, opens in new tab) And that's not public either. Now, if I want to test drive giving inputs to a console application, instead of creating a new process, etc., I can simply use in-memory calls from my unit tests (external link, opens in new tab) to actually call this method, the main method. So if I wanted to try and do this, so let's say I would create a new class here and I'd call it usually I'd put this class in a separate project called string master tests or string master unit tests.
[02:06] (external link, opens in new tab) In fact, you know what? Let's do that. Add new project class library string master unit tests. You don't have to do it. You can just watch for now because you're going to do the whole thing later anyway. For now, it's important that you just get the concept and the idea of what do I what's optional and what what can I actually change to make things more testable.
[02:40] (external link, opens in new tab) I'll just rename this class. And since what I'm going to test is program, the program class, I'm going to say program tests. Also let's set the NuGet package. So, package manager console install package nunit.
[03:16] (external link, opens in new tab) Okay, let's create the test fixture. Test fixture. Create a new test. Now in this test, the unit of work (external link, opens in new tab) is main cuz that's the main static method cuz that's my entry point. And I'm not going to expect to do anything else but just call the entry point and then check that there was some kind of output to the screen. And we're going to find out how to do that. So main now a basic scenario how do I prove that if I send in some inputs to the main method it actually uses string calculator to write to the screen.
[03:53] (external link, opens in new tab) Well remember on the first day I said I don't want to I treat everything between the inputs and the outputs as a black box. So I don't care if it uses string calculator or not. I just care that something was written to the console that is the result. And then I'll just hook up all the internal stuff to make the result happen. So I can say two numbers and
[04:24] (external link, opens in new tab) Writes their sum to the console console. Writes is maybe it's not writes but it calls the console with their sum. The reason I'm using the word calls here is because I can't really use the real console. I'm not going to test this visually. So the console is in fact some kind of dependency, something that I cannot control.
[04:55] (external link, opens in new tab) So, I'm going to have to somehow replace it or I'm just instead of checking that it the console writes something, I'm just checking that the console was called with the correct parameters because if it was called with correct parameters, I can assume that the UI looks the way I want it to look because I don't have control of the UI. I can just replace it with something else. So I use the word calls to to say that there is that the end result (external link, opens in new tab) is a call to an external system instead of just returns something or throws an exception or changes state.
[05:31] (external link, opens in new tab) Now it's a call to an external system that's beyond my control which is a user interface of some sort. Now if I wanted to just call main if I just say program there's no such thing because program is not public. If I go to program I can actually make it public and personally I don't have any program in pro problem in making program public because it's an entry point anyway.
[06:03] (external link, opens in new tab) It's not like it has to be hidden. It's a console application. Now if I go here, ReSharper will tell me, hey, do you want to just reference and use program? I'm going to say yes. But now main is not public either. So I'm going to make it public to make it more visible. So I can actually invoke it. Now again, because this is an entry point, it shouldn't be a problem at all. It's an entry point, so anyone should be able to invoke it.
[06:33] (external link, opens in new tab) It would just wasn't currently meant for the use of unit test. So I'm just making it usable by unit tests. But in other cases where I make things public, maybe it's more of a security risk. Maybe there's a problem there. And in that case, maybe in terms of design I can choose to make things public because I think it's a better design. Or if I think it's not a good design to make it public, but for testability (external link, opens in new tab) I want it to be, I can put it inside
[07:04] (external link, opens in new tab) What do you call these? If like debug or if test like this and if so now only during my tests will this thing be public and callable. So I can put things in in compiler flags if I wanted to hide them. Another possibility if I wanted to make things unhidden
[07:35] (external link, opens in new tab) Is to not make them public but make them internal. Now internal means that it's public only to the current assembly. But I can also add a special attribute in the in the properties of the project inside assembly info CS and kind of say this assembly InternalsVisibleTo (external link, opens in new tab)
[08:11] (external link, opens in new tab) My test assembly whatever the name of the assembly is. And assuming that nothing is strongly named, this should be enough. And then it will look public to a specific assembly because it's been friended. If you have an assembly that has been strongly named, then you're going to have to put the strong the public key. So it looks a bit weird. It's a bit long to generate, but it's still at the end of the day just a string that you put in this attribute.
[08:42] (external link, opens in new tab) And and suddenly anything that's internal becomes public to that assembly. So you have ways around this if you still want to keep things hidden. For us right now it doesn't make any any sense not to just make it public. So if I go back to my tests now program main I can now simply in memory invoke this function. So I can say a new string with the value of one and two.
[09:17] (external link, opens in new tab) Okay. In fact, I don't even need this. So now I'm able to send an input into my console application in memory and it's fully under my control. But how do I know that something was written or something was calling the console application? If I wanted to replace the console application with something else, actually in the .NET API, we have a built-in way to replace the console.
[09:50] (external link, opens in new tab) Now, I'm not sure why this API exists, but probably because someone wanted to test it at the Microsoft headquarters as they wrote the thing. So, they wanted to redirect output to various sources. So what we can do is we can use a special API built in called Console.SetOut. Now Console.SetOut takes a text writer which is an abstract class. So there are multiple things inheriting from text writer. So how do I know which one I I can send?
[10:20] (external link, opens in new tab) What are all the things inheriting from test text writer? This is another thing I like in ReSharper is that I can say text writer and then control alt alt h to see the hierarchy of this class or in this case let's see ReSharper inspect oh type hierarchy yeah yep that should have worked oh because I don't have a using that's why alt h now this is the text writer and
[10:53] (external link, opens in new tab) What I can also do, let's see, is kind of look at this. Apparently, there's nothing that seems to be inheriting from it. Maybe I'm missing something. Cuz obviously there are things that inherit from it that I'm I'm sure there are. So let's say text writer.
[11:28] (external link, opens in new tab) Hm. Interesting. I'm not sure why. Let's see. What if I just say string writer? Well, that that exists. So, I'm not sure why I'm not seeing the type hierarchy right now, but I can tell you that the string writer is an is a version of text writer. Maybe it's a bug or maybe I've just missed something right now. So, I can say string writersw equals a new string writer. Now, string writer writes to a string builder or an in-memory string.
[12:00] (external link, opens in new tab) So, I can say just use my string builder here. So I'm going to create a string builder use it inside the string writer and give it the string writer. Now from now on everything that I write to the console if I do this console.Write blah my string builder is going to contain that string.
[12:30] (external link, opens in new tab) The console is actually not going to be written at all. the output has been redirected. So I've I've essentially replaced the console output with my own output which is a string builder which I have full control of and it's all in memory. So now I can actually assert at the end of the test that the string builder contains a specific string. I can assert that it also equals a specific string. But that's not usually a good idea because that will make the test more fragile. usually just want to test that a string contains something because a lot of times strings are a form of UI.
[13:05] (external link, opens in new tab) So they can contain extra new lines, the dates. We we care about specific things inside the string. So instead of this I'm going to say we call it with main but before that we replace because otherwise if I replace it after the call after the act it's not going to be written to then we can assert and if I wanted to assert on strings there is a a special class I can use built into nunit it's called StringAssert.
[13:41] (external link, opens in new tab) It's built into I can say StringAssert and I can use contains or ends with or matches a regular expression. So StringAssert contains three from the string builder to string. Okay, I can also rename it to let's say fake (external link, opens in new tab) output. Maybe that's a better name.
[14:12] (external link, opens in new tab) Okay, so that's an example of how I can take control of a console output and actually use it as a as a test-driven mechanism to to prove that something should be done. Now, currently, if I run this test, it's going to fail. And I want you to get to this stage where that this test actually fails and make it pass as simply as possible. But in this case what we're doing is we're not going to just use say return three because what we want the purpose of this is to connect two layers together.
[14:49] (external link, opens in new tab) The purpose is to connect the string calculator layer which we built separately and in testdriven into one layer up which is this layer and we can just have a couple of tests that prove that the layer works. We don't have to write all the test for string calculator again. where what we're testing is that we can shoot one thing into the input and we get back another thing in the output and we assume that everything is actually connected because of that. We we it's okay to have some knowledge about the architecture
[15:22] (external link, opens in new tab) But we don't have to care that it calls a specific string calculator class. Okay, questions. Okay, so we're going to split into a pair but it's going to have to be different. So, today you guys can, can pair up and you you'll be working alone today and tomorrow we're going to switch again. Okay.
[15:53] (external link, opens in new tab) So by the way, I wanted to add one more thing in terms of refactoring to make this test. I wouldn't do all these three lines in the test. What I would do is after the test would pass is I would refactor something like this. Extract a method call it replace console and I will change the var to use a string builder to make things more readable.
[16:23] (external link, opens in new tab) So string builder fake output equals replace console call the program and then make sure that the fake output contains something and then I would use that I it would make the test more readable especially if I'm going to replace this multiple times. So it becomes a utility methods