Succeeding with TDD: Pragmatic Techniques for effective mocking
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 150 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/51527 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
CodeSoftware testingExecution unitObject (grammar)Test-driven developmentTwitterTotal S.A.Error messageLibrary (computing)Message passingCodeMultiplication signState of matterSoftware developerBitLinear regressionSoftware testingFeedbackPattern recognition2 (number)Well-formed formulaCalculationOnline helpExecution unitData conversionObject (grammar)WritingSystem callOperator (mathematics)Process (computing)PlastikkarteOcean currentInformationAuthenticationFocus (optics)Markov chainWordLevel (video gaming)CuboidPhysical systemWeightWater vaporProjective planePoint (geometry)Different (Kate Ryan album)RepetitionObservational studyHoaxPhase transitionLibrary (computing)Context awarenessWaveServer (computing)Unit testingComplex (psychology)CausalityGoodness of fitReal numberComputer programmingRight anglePower (physics)Limit (category theory)Sampling (statistics)Dependent and independent variablesSound effectVisualization (computer graphics)MathematicsEvoluteAsynchronous Transfer ModeNumberXMLUML
08:34
Error messageObject (grammar)CodeSoftware testingLibrary (computing)Message passingBuildingVisual systemComputer fileArchitectureView (database)Electronic mailing listSymbol tablePhysical systemVideoconferencingTotal S.A.Source codeSynchronizationLibrary (computing)Flow separationIntegrated development environmentProper mapFeedbackCycle (graph theory)Software testingObject (grammar)Bit rateError messageService (economics)Point (geometry)CASE <Informatik>Web serviceSocial classInformationBitOrder (biology)Internet service providerVisualization (computer graphics)CodeMathematicsWeb 2.0Line (geometry)Right angleMultiplication signVirtual machineDifferent (Kate Ryan album)Series (mathematics)Dependent and independent variablesComputer fileWeightTerm (mathematics)Data miningSpecial unitary groupProjective planePhysical systemOnline helpSource code
13:03
Software testingVideoconferencingView (database)Visual systemSymbol tableElectronic mailing listError messageOvalComputer fileBuildingTotal S.A.Equals signPhysical systemSynchronizationProjective planeLibrary (computing)Software testingCodeSystem callCompilerMessage passingParameter (computer programming)ResultantDot productObject (grammar)Visualization (computer graphics)SpacetimeMultiplication signPoint (geometry)Proxy serverRight angleSound effectFamilyBit rateCASE <Informatik>Goodness of fitComputer animation
15:09
CodeSoftware testingError messageLibrary (computing)Message passingObject (grammar)BitSet (mathematics)State of matterMultiplication signProgrammer (hardware)Interface (computing)Bit rateGrand Unified TheoryImplementationData managementCodeSoftware testingTest-driven developmentRAIDProcess (computing)Software developerSocial classTheoryRight angleSource code
16:37
Software testingOvalVisual systemArchitectureView (database)Computer fileElectronic mailing listError messageSymbol tableVideoconferencingSoftware testingBit rateDoubling the cubeObject (grammar)CASE <Informatik>Descriptive statisticsComputer animation
17:28
Error messageExecution unitOvalEquals signSoftware testingComputer fileWindowBuildingView (database)Symbol tableElectronic mailing listVideoconferencingArchitectureElectric currentBit rateTotal S.A.String (computer science)CodeObject (grammar)Library (computing)Message passingBit ratePoint (geometry)LogicImplementationService (economics)Web serviceTupleString (computer science)Interface (computing)Multiplication signMessage passingMoment (mathematics)Series (mathematics)Web 2.0VarianceComplex (psychology)Focus (optics)Server (computing)Software testingDependent and independent variablesRight angleCASE <Informatik>WordNP-hardRAIDWeightFunctional (mathematics)Computer animation
20:14
String (computer science)Software testingComputer fileBuildingWindowView (database)Electronic mailing listSymbol tableError messageVideoconferencingOvalBit rateOnline helpTupleMultiplication signString (computer science)Bit rateResultantTupleSoftware testingDirection (geometry)Interface (computing)CASE <Informatik>Decision theoryBitWordEqualiser (mathematics)Set (mathematics)Execution unitValue-added networkObject (grammar)Computer animation
22:32
Error messageBit rateOvalSoftware testingWindowArchitectureView (database)Visual systemString (computer science)Symbol tableElectronic mailing listVideoconferencingForm (programming)Control flowSocial classEuclidean vectorWeightInterface (computing)Data typeInstallation artCodeTraffic reportingPhysical systemOnline helpSynchronizationComputer fileBuildingTotal S.A.Instance (computer science)Parameter (computer programming)Function (mathematics)Operator overloadingData conversionSpacetimeService (economics)Doubling the cube8 (number)Web serviceCodeService (economics)ImplementationObject (grammar)CASE <Informatik>Multiplication signBitSystem callParameter (computer programming)Interface (computing)String (computer science)Error messageTupleBit rateType theorySoftware testingLibrary (computing)Eigenvalues and eigenvectorsSocial classInstance (computer science)InjektivitätConstructor (object-oriented programming)Equaliser (mathematics)Factory (trading post)Right angleOrder (biology)Spring (hydrology)File formatDoubling the cubeTask (computing)MultilaterationField (computer science)Point (geometry)Metropolitan area networkReading (process)Different (Kate Ryan album)ResultantComputer animation
30:50
Object (grammar)CodeSoftware testingService (economics)Error messageLibrary (computing)Message passingOvalBit rateMountain passVideoconferencingString (computer science)Computer fileBuildingView (database)Visual systemSymbol tableElectronic mailing listHyperbolic functionStructural loadArchitectureWindowSource codeOnline helpException handlingError messageService (economics)Server (computing)CodeFlagObject (grammar)BitCASE <Informatik>ResultantSoftware testingComplex (psychology)Shape (magazine)WritingComputer clusterC sharpInheritance (object-oriented programming)Social classCartesian coordinate systemMultiplication signSystem callLevel (video gaming)Exception handlingPoint (geometry)Bit rateOrder (biology)Different (Kate Ryan album)Formal languageComputer programmingMultiplicationString (computer science)Right angleUnit testingScaling (geometry)Condition numberQuantum stateAdditionEndliche ModelltheorieOctahedronExecution unitJava appletProcess (computing)Markov chainSource codeComputer animation
37:25
Software testingCodeService (economics)Error messageLibrary (computing)Message passingHoaxType theoryLibrary (computing)BitWordSoftware frameworkSocial classMultiplication signVisualization (computer graphics)Object (grammar)Software testingRight angleSet (mathematics)Source code
38:29
Software testingWeightBit rateVideoconferencingSource codeVisual systemWindowBuildingView (database)Computer fileString (computer science)Symbol tableElectronic mailing listError messageOvalException handlingService (economics)HoaxObject (grammar)Web browserMountain passTupleSoftware testingCodeElectronic signatureLibrary (computing)Social classHoaxService (economics)Real numberInstance (computer science)Point (geometry)Parameter (computer programming)ExpressionString (computer science)Order (biology)Lambda calculusSystem callBit rateMultiplication signCategory of beingCartesian coordinate systemObject (grammar)Exception handlingCASE <Informatik>MereologyFunctional (mathematics)Information overloadBitEvent horizonProcess (computing)Uniqueness quantificationMetreQuicksortOcean currentComputer animation
43:24
CodeSoftware testingError messageService (economics)Library (computing)Message passingObject (grammar)Bit rateExpected valueVideoconferencingComputer fileWindowArchitectureView (database)Symbol tableElectronic mailing listSource codeVisual systemOvalString (computer science)Mountain passTupleSynchronizationPhysical systemHoaxException handlingBitCodeSoftware testingSystem callResultantDoubling the cubeString (computer science)Multiplication signObject (grammar)Set (mathematics)Bit rateCode refactoringService (economics)Proper mapLevel (video gaming)Different (Kate Ryan album)Data dictionaryRight angleMountain passCASE <Informatik>CompilerException handling2 (number)Mach's principleComputer programmingDivisorSpring (hydrology)Statement (computer science)Point (geometry)Library (computing)MultiplicationSource codeComputer animation
47:56
CodeSoftware testingExecution unitError messageLibrary (computing)Message passingException handlingBit rateOvalSource codeMountain passComputer fileWindowArchitectureBuildingView (database)Visual systemSymbol tableElectronic mailing listVideoconferencing10 (number)Equals signObject (grammar)Maxima and minimaSimulationRankingHoaxData dictionaryTelecommunicationService (economics)Host Identity ProtocolOnline helpMobile appError messageMathematicsConstructor (object-oriented programming)MereologyRight angleMultiplication signService (economics)Exception handlingComplex (psychology)InjektivitätCASE <Informatik>CodeSoftware testingMessage passingComputer fileBit rateSystem callRoutingContext awarenessBlock (periodic table)ImplementationLibrary (computing)Interface (computing)Object (grammar)Expected valueHoaxSimilarity (geometry)VarianceShared memoryCoefficient of determinationWeightPhysical lawData structurePhase transitionCausalitySoftware developerVideo gameComputer programmingProcess (computing)HypermediaLoginNamespaceServer (computing)Computer animation
54:37
Software testingSimulationComputer fileView (database)Error messageElectronic mailing listSymbol tableVideoconferencingSource codeHoaxInformationDirectory serviceMessage passingWindowOnline helpBuildingMountain passArrow of timeHand fanException handlingFile formatUnicodeParsingStreaming mediaPhysical systemInstance (computer science)Software testingComputer filePoint (geometry)Message passingWeb 2.0Real numberError messageCodeFile formatString (computer science)Right angleContext awarenessException handlingService (economics)Power (physics)InjektivitätParameter (computer programming)2 (number)CompilerPhysical systemObject (grammar)RandomizationHoaxSocial classMultiplication signBit rateDifferent (Kate Ryan album)Interface (computing)Web serviceInstance (computer science)LoginCompilation albumStress (mechanics)CASE <Informatik>Figurate numberAsynchronous Transfer Mode40 (number)Function (mathematics)WebsiteLibrary (computing)NeuroinformatikComputer simulationCommutatorComputer animation
01:01:18
Software testingExecution unitCodeObject (grammar)TwitterTest-driven developmentSoftware developerFigurate numberCodeCombinational logicSoftware testingDifferent (Kate Ryan album)Computer animation
01:02:05
XMLUML
Transcript: English(auto-generated)
00:15
A session on Succeeding with TDD, Pragmatic Technique for Effective Mocking.
00:21
My name is Venkat Subramanyam. I'm going to just talk maybe for about five minutes, a little bit more maybe, and then we'll get into code and the rest of the time we'll take a look at an example and then drive it through test room development and mock objects. Best time to ask a question or make a comment is when you have it, so please don't wait till the end. Any time is a great time for questions or comments.
00:40
Please don't hesitate. So, talk a little bit about benefits of unit testing. I won't spend too much time on this topic, but just to quickly highlight why we want to do these things, the two things that I really like about test room development is the two clear benefits in my eyes are one is regression. You want to make sure that the code that you wrote once
01:00
continues to work as you evolve and modify the design. The second benefit of TDD, which takes a little bit more effort, a lot more effort, is to make sure that our design is actually practical. Design is lightweight, so test room development can benefit by creating, or help us to create, a better quality design.
01:21
But that doesn't come too naturally, at least for me. It requires a little bit of effort in getting that. But the regression aspect definitely is there. As we evolve the design, it definitely gives a feedback to say that the code actually not only worked before, but continues to work. In a lot of ways, it gives us an ability to do fearless evolution of code
01:41
because within a short span of time, it gives a rapid feedback that the code actually is working as well. That has definite benefits. Now, of course, when it comes to unit testing code, unit testing is very easy when the code we are trying to test is independent or isolated.
02:00
If you have a little code that does a formula calculation, a mathematical operation, currency conversion, what have you, you may say this is easy to do because it doesn't have any dependencies on other things once all the information it needs is given to it. So it's very easy to unit test code that has no dependencies.
02:22
Unit testing code that depends on other code becomes incredibly difficult. What if you have a piece of code that has to talk to a database, a piece of code that has to talk to a remote server, a piece of code that requires authentication to something before it can get the data, a piece of code that has to process credit card, and then based on that has to do other operations.
02:42
All these code can become very difficult to unit test. It becomes brittle because we also have these dependencies. Not only can it become slow. And oftentimes, how do we solve the problem? We quietly say this code is not testable. Well, one of the things I've realized over time is when somebody tells us this code is not testable,
03:01
that is actually a euphemism. What they are trying to tell you is the design of the code actually sucks. So if a code is designed fairly well, we can actually test it. So it's important for us to be able to write code and design code in a way we can actually test it. But how do we go about really writing these kinds of code
03:21
where it can be tested? Now, this is where mock objects can help us. But before I talk about mock objects, I want to emphasize one thing. I don't want to kind of mislead people to say mock objects are great and use them. That's not what I'm saying. Mock objects can come to our rescue, but we have to be very careful using them. In this entire presentation, I'm going to focus on using mock objects,
03:43
but I don't want to carry you towards saying, oh great, use them all the time. That's certainly not what I'm saying. Be very sparing in using mock objects as well because I've seen projects where mock objects become really difficult and they get into this mode called a mocking hell and it becomes really hard to maintain code after that as well.
04:03
So be very sparing, be very cautious in using them. So what are mock objects really? Mock objects are objects that can stand in for other objects. So let's say for a minute, I want to make a good movie with a famous actor. Obviously, it's going to be expensive to set up the stage with this actor on stage,
04:20
so we'll probably walk around the street, find a guy about the same height and weight and say, hey, would you like to come and stand in and you have an opportunity to look at the famous actor at the end of the day? So this person of course comes and stands and helps you set up the stage and obviously this person is not as good as the famous actor is, otherwise you would be paying big bucks for this guy to come and stand in as well, isn't it? So the point really is that it is a stand-in,
04:42
something that comes and stands in for a real object. There are some seats available here as well, but if you're more comfortable sitting, that's perfectly fine. I'm not saying you shouldn't. A lot of times I sit and I don't even wear shoes, as you can see. So make yourself comfortable any way you want to, but there are seats available here. So the real idea is to stand in for real objects
05:02
so that we can accelerate testing. Now, I don't want to spend too much time on this topic either, but I do want to draw a quick distinction between stubs and mocks, mainly because we face these kinds of concerns, but the difference between a stub and a mock, as I see it, is Martin Fowler describes this fairly well.
05:22
He talks about a stub represents a state for testing purpose, a mock represents a behavior for testing purpose. So the real idea is, if you're really interested in an object, just standing in for another object, a stub is quite enough. But a mock object, as I see it, tattletales to you. It says, let me tell you what really happened.
05:41
Here are the methods that were called. Here are the number of times the methods were called. So it gives you a lot more detail. Now, having said this distinction, invariably, I don't get too pedantic about it. Yes, there are differences between stubs and mocks, but I really don't care. So most of the time, I use the word mock very loosely, and there are times when I really am using a stub
06:02
and I use the word mock. I don't want to be sitting there and thinking too much about it, and things kind of fall in place most of the time. So when you hear me say the word mock, it could be a stub sometimes, it could be a mock at other times, but it really doesn't matter most of the time, as long as we kind of know the difference within our own context. So how do these mock objects really help?
06:22
There are two ways in which mock objects help. The first way mock objects help is they stand in for good behavior. For example, let's say I want to process my order, and I need to talk to a credit card system. I don't want to talk to a real credit card system because of all the complexities involved in it, so I will put a mock object in place and say,
06:42
for any request that comes in, simply approve these requests, and it can simply respond back with an approval. And I can run my test very quickly with this mock object. But more important, a mock object is also very helpful to stand in for bad behaviors as well. For example, what are the possible things that could go wrong
07:01
when I try to communicate with a remote system that is going to process my credit card? Well, what is it that cannot go wrong, right? Any time you turn around the corner, there's Murphy sitting there saying, here are things that could go wrong. But how do you know that your program handles these properly? Well, you can set up a mock object to start failing in a very predictable way so that you can make sure your code
07:21
is handling these failure situations. So when your code does talk to a real system, those failures can be gracefully handled. So mock objects can pretend about bad behavior that you may expect as well in a very deterministic manner as well, and that becomes very helpful. That's all I'm going to talk about for the most part, but I think the real fun is really trying things out.
07:42
So for the rest of the session, I'm going to try out an example, and I'm going to create a little sample code, play with it. Let's start somewhere and see what we can do. So I'm going to use an exercise, a sample exercise, to create and play with it. And I've been struggling with this for a while with various tools and techniques, but I also ran into something that is kind of interesting
08:02
in Visual Studio 2012, which is the Fakes library, which really gives a very interesting way to mock out things. I've used Rhino-Mock quite a bit in the past. I really like Rhino-Mock, but there are times when I kind of hit a few limits on Rhino-Mock as well. But Fakes seems to really bring an enormous power on our hand.
08:22
But of course, remember the wise words of Uncle Ben, with great power comes great responsibility, so we've got to use it wisely as well. But let's take a look at how we could use it as well with an example. So here is an exercise that I would like to start with. In this exercise, what I want is I want to implement a currency exchange object.
08:41
So fairly simple to this currency exchange object. I'm going to give a name of a currency, and I want to know how many US dollars I'm going to get back from this currency. Well, in order to do this, of course, my service provider here is running a business where they want to communicate with other providers, really, and then find who offers the best rate,
09:00
and then mark down the price by 2% to get a profit, and then return back the remaining money. So this code is going to go to a service and say, hey, vendor one, what kind of currency rate can you give for me? And they say I can offer you a $5.22, for example, or $5.22. And then I ask for vendor two.
09:21
Well, that happens to give an error today. So I log the error information. I ask for vendor three. It gives me a rate. I mark it down by 2% and return back the vendor three and whatever the value is. So this is what I want to implement. Very simple example, but there are some challenges already.
09:40
There is a little bit of math I have to code, but I also have to talk to a web service, which is out there somewhere on the web, and assuming I'm connected to the web, I should be able to connect to it as well at the very end, hopefully, and we can see it. But how do I go about designing this code? So I'm going to take a test-driven approach to design this, and keep a check on me.
10:00
I should not write any code until I have a test failing, and I'm going to write a test after that, and then we'll slowly introduce it. So I'm going to go through a series of steps to make this example work. And when we are done with it, I'm hoping we'll have the scenario working where we'll go to the web service to talk to various different objects. If there's an error, we'll log it to a file,
10:21
an errors file, and then we'll return back a good response to the user saying, here's a vendor that I'm going to provide for you, and here's the marked down price after we took the profit away from the cost that we're going to give it to you. Seems reasonable so far? All right, let's go ahead and start with something. Where do I start? I normally want to start with what's called a canary test.
10:41
What's a canary test? A canary test is a little test to make sure things are actually set up on the machine. I work with a lot of different companies. I work on projects. And when I go over there, I find that a lot of times tools are not set up properly. And a canary test is a good way to make sure things are set up. It's a stupid test. You say assert true is true.
11:02
I mean, how stupid it would get beyond that. And if assert true is true actually fails, you know you're going to have a very long day trying to fix the system, right? And then you can have people come over and take a look at it, and you say, this is not working. Can you help me set up the system? And they will ask you, what are you doing? And you show them the code, and they say, that's stupid. Yeah, please help me make that work really, right?
11:20
So you're not writing 50 lines of code and then messing with it. So let's start with a little canary test to begin with. So all I have here is a test class called currency exchange test. That's the name of the class. And I want to write a test over here. So test, I'm going to write a test method right here. I'll call it a canary test. And all I'm going to do in the canary test, as I promised is,
11:41
I'm going to simply say assert true is true, as simple as that. And I'm going to go ahead and run my test right now and see if that test wants to run. It's building the test at this point. And of course, this test, if everything goes well, should pass, and it should tell me that the canary test is passing. And so right there is the canary test.
12:00
Today is my lucky day. You can hear the canary sing. Well, the term canary test comes from this little phrase that canary in a coal mine. So the miners take this little bird canary in a little cage, and they go into the mines. And of course, as they are working in the mine, sometimes there could be a bad fume, which is dangerous to the health. The canary keeps singing all day long, and if it smells this fume, it dies.
12:21
This is very good for the miners, not so much for the canary, unfortunately. But it gives a quick feedback cycle. That's what we are doing here, is a little canary test. So it looks like my environment is set up properly. I'm using Visual Studio 2012 here. Not a whole lot. I have this test class called CurrencyExchangeTest. It's in a separate test library, by the way.
12:41
I also have this other file here called CurrencyExchange, which is in its own libraries. I have two separate libraries here. One is the test library, and the other is a normal .NET class library. That's all I have on my hand at this point. Okay, great so far. What's the next thing I want to do? Well, I want to implement the simplest case.
13:00
I want to implement a markdown. So let's go ahead and write a test for a markdown. So what I'm going to do here is simply say, I want to write a test for markdown. So we'll call markdown over here. Now this should be fairly simple to get going. So what am I going to do within the markdown itself? I'm going to give a little space here so it kind of stays in the middle.
13:21
All right, so I'm going to create a currency exchange over here. So currency exchange is my object. And in this case, I'm going to say currency exchange and equals new, let's say currency exchange object. And of course, Visual Studio should complain to us that it doesn't have a clue what currency exchange is. We can quickly fix that. Let's go here and add a reference to this.
13:42
So I'm going to go to my test library, add a reference. It should be in the solutions to the project itself right there. I select that right now, and that's all I'm doing. I'm bringing that back over here. So that should be enough for our purposes. So let's go ahead and bring it. So in this case, of course, I added the library. So let me go ahead and ask him to bring in the using for it.
14:01
So that should please the compiler. So now I'm going to simply say currency exchange dot. I'll call markdown. So markdown is going to simply accept some value of currency. And of course, I want to make sure this is giving back the right result. So we'll say r equal. And of course, 2% is taken away.
14:20
So that should be 98 out of the 100 that I'm interested in receiving back. So let's go ahead and implement the markdown method. Of course, the test is failing already. You can see the compiler didn't even like the code. So let's go ahead and make that test pass. So I'm going to go ahead and implement this markdown. I'm going to take an amount as a parameter. In this case, I'm going to simply return.
14:42
And let's go ahead and return amount times 0.2%, 98% rather, isn't it? So 0.98, and that should take care of it. Of course, you may argue, don't you want to just return the value itself and then write more tests to do it? Absolutely, we could do it. But I want to move on to other important tests to work on.
15:01
So I'll kind of bypass those steps right here. So our markdown is working as well. So that's the good news. So it's time for us to get to a more interesting test at this point. What about getting the rate for a currency itself? Now, what if there are no vendors? So notice that I want to start from very small steps. I always content, you know, confront with this.
15:22
What am I really doing in testing and development? I spend a lot of time talking to programmers because we learn a great deal when we talk to each other. So in one of the discussions, I was asking, how do we really approach testing and development? And what I heard was something that intrigued me. TDD is an approach where we go from a set of unknowns to a state of known.
15:43
And I thought that was a really nice way to think about it. But the way I want to emphasize that a little bit more is I want to go from a state of unknown to a state of known, but only looking at one small unknown at a time in manageable pieces. So that really makes a nice path for us to walk through
16:01
and build on what's working already. So I start with the no vendors to begin with because I want to say I don't have any vendors at all. That's easy to write, not a whole lot of code to write, and I can get the interface set up. Why go through these little steps? The reason why I like to go through these little steps is the very first few steps really help me to build an interface of a class,
16:24
and then as more tests are written, it starts forming the guts of the code itself. So we go from the outer skin of the interface to the inner implementation by going through this approach. So I tend to really take this approach to develop it. So I want to write for no vendors. So I'm going to write a little test here that's going to be for rate given no vendors,
16:47
and how do I implement this rate given no vendors over here? So let's go ahead and say, again, a currency exchange. I'm going to create an object of this currency exchange. So in this case, of course, I'm going to say the currency exchange
17:01
is going to have a method called get rate, let's say. So get rate is going to take the currency I'm interested in. We are assuming for a dollar of rate that we are interested in giving here. And what is this going to give me? I'm going to simply say double rate equals, and it gives me a rate value.
17:20
But I want to assert and get whatever the value of the rate is, so I'm going to say r equals 2. But remember from our problem description, we had two things coming back, the vendor name and also the rate itself, so we need to take care of that. So I'm going to say var rate over here. But what is the rate going to be? Well, I want the rate to be something that I would expect to be reasonable,
17:42
but as a web service, as a service that relies on web services, what is a good response I have to give? Well, it makes absolutely no sense to complain to the user that the web sucks and the server is down, because the user cares less. So one way you can really convince the user not to do business with you today is you can say, I'll take all your money and give you back nothing.
18:03
That would be a very clear message if something is totally wrong. So I'm going to simply return back over here a rate value I'm going to expect. So I'm going to say expected equals, and of course, all that I have to verify here is that the rate I got back is expected. But what is this expected value, by the way?
18:21
The expected is going to be just a tuple, by the way, and the tuple of a string and a double value. And in this case, all that I would like to give to it is a no vendor name and a 0.0 value given to it, and that should be quite adequate for our purposes right now. So this kind of defines the interface of this function.
18:41
Taking a get rate function, which takes a string, which is a currency name, returns back a tuple of these values. Let's go ahead and implement this function, by the way. So I'm going to go ahead and ask him to generate the step for me. If I go back over here, it has the get rate method, which I want to return a tuple of a string and a double value, as you can see here.
19:03
And then of course, in this case, I'm going to say currency, which is what I want to send to it. And this is going to be a fairly simple implementation at this point. We'll simply return new tuple, and this is going to be an empty name and a 0.0 as a value, and that is all I need to really return, and we can see that test is really passing.
19:21
So we implemented the interface for this with no vendors, and we defined what it should do when no vendors are given. The next step here is to implement for one vendor. So we're kind of gradually adding the complexity. What if I do have one vendor? How do I want this to implement? Remember the problem on hand one more time. When there is a vendor, I have to go talk to the web service.
19:42
But I don't want to talk to the web service at this time. And the reason simply is because I want to keep my focus on my logic I'm implementing. I don't want to worry about the web service at this point. I kind of know what the web service should do for me. I don't care to know how it is going to be. I'm going to communicate with it at this moment
20:00
so we can gradually move again from a series of unknown to solving one unknown at a time and then going towards knowns. So what am I going to do to avoid that particular concern? So to avoid that concern, I'm going to go ahead and create an interface because how do we really deal with dependencies? Well, one way to deal with dependencies is to eliminate it.
20:22
By the way, that's a very important thing to consider. A very important design decision is not to invert dependency but to eliminate it wherever possible. Because the more dependency we deal with, it's a pain. But dependencies that we cannot eliminate, we can invert the dependency by throwing in an interface.
20:41
And of course, an interface stands in really nicely and then we can then implement it the way we want to implement it. So how do we go about implementing this? So let's go ahead and write our test case first. So in this case, I'm going to say the test I want to write here is a rate given one vendor.
21:00
Well, how do I implement for a one vendor? So I'm going to go ahead and introduce the currency exchange one more time here. You could argue that we could use a setup method and remove dependence duplication of this. Fair enough, but I'm not going to go that direction right now because that's not going to help us go in the direction I want to go. So I'll kind of avoid that for now. So I'm going to say expected equals new tuple one more time.
21:24
And in this case, it is going to be a string and a double as well. But this time, I'm going to say vendor one is the vendor we selected and I want this amount to be 98 units of currency. So that is my expected, and I'm going to say rate equals currency exchange dot get rate one more time.
21:43
And in this case, of course, I would like to get the rate value asserted. So we'll say assert r equal one more time and then expected and then rate that I'm expecting back. But looking at this test, how in the world do I know that this is going to be a result of vendor one and the value of 98?
22:00
So we got to do a little bit more preparation for this. So I'm going to say currency exchange dot and in this case, of course, I have to set up the details for the currency exchange. So I'll say set vendors over here. And in this case, of course, I'm going to pass to him the vendor one itself that I want to pass in and say I want to tell you that I have one vendor available to me
22:21
but go ahead and find me the results for me. So given this value, but still how does it know that the value should be a 98? We haven't given that detail to it yet. So the idea really is this. We want to go to this object that we are testing right now, ask him to give a price. He knows that there is a vendor one. He's going to go to the web service and say,
22:42
hey, I'm going to give you a vendor one. Can you give me the price back to me? So to make this code testable, we want to mark out the service that we have on our hand at this time. So how do I mark out the service? Well, to mark out the service, all that I have to do is simply write a little implementation that says
23:00
given this vendor name, just simply return back a price of 100, and then as you mark it down by 2%, you get back a 98. So we can easily work through that very well in this case. So to do this, I need a mock object. But let's wait a little bit before we can introduce that mock object over here. So to make that happen, I'm going to introduce an iVendorService over here,
23:24
and I'm going to call it vendorService right here. And this vendorService, I'm going to implement this in just a minute, so I'll just leave it as a null for a second, and I'm going to say this is a vendorService, by the way, that I want to pass to the constructor. So we're using this dependency injection through constructor.
23:41
There are but three ways to introduce dependencies. Well, maybe more than three ways, but one is to introduce it through a constructor. The second way to introduce that would be using a setter method on the class. A third way probably is to use a factory under the hood where you call the method you are interested in.
24:00
That calls into a factory, and the factory will give you the instance you are requiring. This is where other approaches like Spring and so on comes in for injecting the dependencies. So in this case, I'm going to do a very simple constructor-based injection. But in order to do this, I need a vendorService, by the way, but where is this vendorService? Well, it's pretty easy.
24:21
I'll go into the library code, and I'll go ahead and add in this case, and I will simply add an interface that I want to create in this case, and this is going to be called a vendorService. It's going to be iVendorService, so iVendorService is what I want to create, and I want this to be an interface in this case.
24:41
Let's even make that a public interface. We can come back to the methods of it a little later. No rush to work with it. So that is the interface I just added right here, and I'm passing it to this particular class. Of course, for this to work, I need a constructor that takes this particular argument, so let's go ahead and add it. We'll create one constructor which doesn't take any parameters
25:02
because we used that in the previous test cases. We'll create yet another constructor where this constructor takes a vendorService, and I'll just say service here, and I'm going to simply assign it to a vendorService equal service right here. Well, this vendorService is going to be just a field I want to create in the class,
25:21
so right there is the field I've created to hold the vendorService itself. Now let's get back to the test itself and see what we're going to do here. Well, we are good so far. I need the setVendorService method. Let's go ahead and implement that method, and now to get to this point, what about this vendorService I need to implement it? Well, that's going to be a little mock object,
25:41
so I'm going to be lazy and create it right here, and I'll call it as mockVendorService, and this mockVendorService simply implements the vendorService itself, and we'll come back to look at what it should do a little bit later. So I got this vendorService, and I'm going to simply say new mockVendorService and create an instance of it.
26:00
So we have the code in place so far. We have created a mock object for the vendorService, and we have told him that I want to use that object here, and we are passing the vendors to this class and using it, so we have to go back and implement this method properly for it to work. So let's go ahead and create a vendor for a minute. We'll call it a vendor, and in this case,
26:22
I'm going to create a vendor, and let this be just an empty string for a minute. Actually, let's leave it as a null for a minute, and then, of course, in the code, I need to tell him to use the vendorService. Where do I set it? In the setVendors method, let's go ahead and set the vendor, so I'll say vendor equals the given vendor itself.
26:41
So I'll just take one vendor for now, and on this case, of course, I have to use this vendor in my code. So how would I use this vendor itself? So right there is the string vendor, and I've assigned the vendor over here. I'm not sure why he's complaining. Oh, there we go. My eyes are fooling me. And, of course, I have to use this. So what am I going to do in the method getRate?
27:01
Well, that's fairly simple. So I'm going to first of all create a rate over here, and I'm going to simply return this rate value. So we'll simply say return rate. But in between, we will say, if the vendor is not equal to a null, then I'm going to go ahead and use this vendor to get the rate itself.
27:21
But the vendorService, by the way, is the mock object, so we can readily use that, isn't it? So I'm going to say here, amount equals, mark down on the given amount, but where is the value itself? In the vendorService, and I'll call a getRate for vendor, which doesn't exist right now,
27:40
and we give him a vendor name, and then we also give him the currency name as well. So we are slowly defining the behavior we expect of the interface, which will be implemented by our mock object. Now, you may argue, don't you have to know what the real service you're going to depend on provided? Sure, we do. But it also only matters if it serves our purposes,
28:02
so we'll kind of drive it from our need and then map it over to what it provides when we get to that point. So in this case, I have a getRate for vendor, and we have to implement it, but once this is done, my next task would be to create the rate itself in the format we are interested in. So new tuple over here,
28:21
and then in this case, I will say this is the vendor name, and then the amount is the rate itself that we want to pass out. So this requires us to implement the getRate method, which is fairly simple to implement because it just becomes a method on the interface itself, so that is pretty adequate for our purpose. So we are almost there to get this running.
28:41
So what is going on at this point? Well, we have the rate value sitting up here, and we want to create a new rate over here and then set the rate value. Oh, let's make sure this is returning the proper value in this case. So let's get to this method, the vendor rate itself, because I think I messed up the return type of this. There we go. Actually, I didn't. So it's a double.
29:01
This is going to be a currency. So that's okay. So let's see what the error here tells us. So the tuple string and double, the vendor is good and the amount is going to be double, so he is not happy with the string and double here. Do you see the problem here? I don't see the problem. We'll see if he complains a little bit more. Sorry, what was that?
29:22
Oh, of course, markdown, what does it do? Markdown returns... I didn't notice that. Oh, how bad that is. Double, of course, yeah. So let's go back to this and see if that's satisfying him. So markdown is going to return double. Still he says invalid argument. Let's make this double here.
29:41
And then this is going to be a string, which is a vendor. So let's go ahead and try this. So let's see, does not implement the interface. Yes, okay, finally we are getting the right error we should be getting in the test method itself. So in the test, what are we having problem with? In the test, we have this interface.
30:02
We need to implement this finally. So to implement this interface, we'll ask him to provide an implementation for the getRate method. All that we have to do is simply return 100 after all here. That should be quite sufficient for our purposes. So we have the test for one vendor working now.
30:20
And what did we do so far? We created a mock object. We kind of avoided the service at this point conveniently. And then we said, given this particular mock object, which simply returns 100, in this test, we are going to go ask him for a price for the vendor one. And we have told the class there's vendor one available.
30:41
And the getRate returns 100. Sorry, getRate goes to this guy and gets 100. Marks are done by two. And we got a 98 that we are returning. So our first test is good. But we are ready to really think about what if the service actually fails. Because when you're sending that request to the service, something could really go miserably wrong and starts failing.
31:02
Well, the problem is the service may have different kinds of errors. It may give a blank result, some garbage at some time, what have you, a server error. How do we deal with it? So what I'm going to do here, let's go ahead and copy this for a minute and start changing this to give an error situation. So rate given one vendor and service gives error, let's say.
31:25
How do I deal with that? Well, if I start with this, when the service itself fails, we are back to square one because I only have one vendor available. Even that vendor failed. So my result is going to be an empty string and a 0.0
31:41
because a user doesn't care that it went wrong. I care about it, right? So vendor one is given, and I'm going to go ask for the rate. When I run this little example, of course, the test should fail right now, and it did fail. And if you look at why it failed, you will notice the reason for the failure here is that we were expecting an empty and a 0,
32:02
but the result was a vendor one and 98. So that clearly tells us that we are not really processing the error in this case, but where did we even tell him this will go wrong? Well, obviously, our mock was nicely returning 100, so we've got to tell our mock to return an error. How do we go about telling our mock to return an error?
32:22
Well, if I go back to this mock and say, throw new application exception over here, and in this case, I'm going to say simulated exception, and I could go ahead and remove this return 100 for a minute.
32:41
If I do this, what's going to happen? Well, both the tests actually fail. Why is the other test failing? Obviously, because we have to return 100 and not throw an exception. So now we are in a little bit of a pickle, isn't it? How do we deal with this? Any ideas? One suggestion, you're absolutely right.
33:00
Before we go that, I'm sure we are all tempted by this, right? Why don't we make this mock throw an exception in one condition and return a result in another condition? Anybody else tried that before? Absolutely, right? One brave one raising their hand over there. Absolutely, we all have tried it. A few more people raising their hand. That's okay among friends. Nobody's going to blame you for that, right?
33:21
So we all have done that. Now what will happen if we do that? How do we tell him to throw an exception in one case and then return a value in another? Well, we've got to probably put a little conditional flag in there. And then we'll tell him here's a conditional flag. And then we will come over here and say bully and throw exception or don't throw exception.
33:41
So our mock grow a little bit bigger right now. Then we go to another test and that test wants a little bit more. So what do we end up doing? We slowly start putting a little bit more into this mock. And one morning you come to work and suddenly this mock has grown and taken a shape in front of you. And you're a little kind of worried about it.
34:02
You sit a little away from it and say that kind of has grown really scary, isn't it? It started really small and benign. And then you know that you've really gone over when one of your colleagues comes to you and says I've got a question for you. Do we need to write unit tests for the mock, right? That's when you know you've really gone overboard with this, right?
34:24
So the moral of the story is this. Never make a mock more complex. I was actually teaching a course a couple of years ago. And when I started teaching about mock objects, one guy said I've been using mock objects for a few years and I hate them. I said that's very motivating.
34:41
Thank you for setting up the stage for this session. But we'll talk about it when I'm done with it. Right as it was in the middle of the session, he raised his hand again and said, I know why I now hate mock objects because I always took a mock object and started growing them and over time it becomes overly complex. So the first thing to avoid doing is
35:00
don't create one mock object for an object. So what do we do then? If you think about an object you are standing in for, you want one mock object for satisfying one test. You want another mock object for satisfying another test and yet another mock object for satisfying yet another test and so on.
35:20
Entertain the thought for a minute. It also leads to a few more concerns, but we'll come back to that. So what I'm going to do to avoid this problem is let's get back to running this test one more time and the other test is still passing. That is good. We don't want to break an existing test. So I'm going to say here we'll call it as a mock vendor service that blows up, right?
35:41
So a mock vendor service that blows up is what I want to call here. So how would I create this mock vendor service that blows up? So here is my code. I'm going to say class mock vendor service that blows up inherits from iVendorService again. And in this case, of course, I'm going to go ahead and implement this method,
36:02
but in this method I'm going to simply say throw application exception and I'm going to simply say simulated exception over here. So let's go ahead and say that exception. Well, that's great so far, but there is one small problem in this case. Well, in order for this to work properly, of course,
36:21
we've got to modify our method. So I'm going to go to our method itself and say, well, here we are. If I'm going to go ahead and try accessing this, by the way, and if this was giving an exception, catch exception, so we'll say exception because it's going to fail at some point. If it does fail with an exception, in this case I'm going to simply just skip it.
36:42
We'll worry about what to do. Remember our requirement is we have to log errors. We're not ready to write that right now. We'll just put a skip here for now and move on. So let's go ahead and run the test one more time and see what it does. And we can see our test is passing, so we got to that point right now. But we solved the problem, but we introduced another problem at this point.
37:01
Now, when I'm programming Java, by the way, I'm programming multiple different languages. When I'm programming Java, what I normally do is I use anonymous inner classes. So within my test itself, I would create these mock objects so they're kind of close to each other. Well, C Sharp doesn't quite give us that kind of flexibility, and so we are kind of tempted to put this class outside.
37:21
But the biggest pain I see going this approach is that when we do this, we're going to be creating several mock classes. Now, nobody in their right mind would want to see 300 mock classes sitting around. It's kind of scary to think about it. So this is a time when we start looking for a tool or a library,
37:41
and then you would say, should I use a Rhino mock? Should I use a type mock? Whatever that you want to mention. In today's world, you take the word mock and put a word in front of it. There's a framework or library with that name, right? So you will go find some tool and use it. Like I said, I've used Rhino mock in the past, and here's when I would scale into using a Rhino mock. So what would I be doing here?
38:00
Within my test itself, I would create a mock object and use it. I'm going to do something a little different here. Rather than using Rhino mock, I'm going to use a fakes library in Visual Studio 2012. The reason I'm going to use that is I'm going to get into a little bit more complex situation later on in the session,
38:20
and it is much easier to work with fakes in those situations, so I'll come back to that. So I'm going to use a library here, but the library I'm going to use is the fakes library to do this. Let's take one test at a time and convert it over, and when I'm done with it, these two mock classes would simply disappear. So let's get to the first test, by the way, because that's a simple test.
38:40
What does it do? It simply says, hey, mock, simply return to me 100. That is all your job is. So notice what I'm going to do. I'm going to go in here first of all, and in the test library, I'm going to find the library I depend on. So this is the test library. This is the library I depend on, and I tell him to go ahead and add fakes assembly.
39:01
So this really brings in a fakes library, which allows us to easily create mocks in this particular case. So now that I've added that, it generates a piece of code under the hood, and we can start using it. So in this case, what I'm going to do is to remove this part for a minute, and I'm going to simply say equals new. Rather than creating a mock vendor service instance here,
39:24
I'm going to simply say new, and this is my library, which is the currency exchange library dot, fakes dot, and I'm going to say give me a stub for the I vendor service. So I'm asking him to give me a stub for the I vendor service class itself.
39:41
So instead of creating my own mock class, I just relied upon the fakes library to create it, and I just instantiated right here. So the burden of me creating the mock class separately just disappeared. But of course, I have to tell him what to do. How do I tell him what to do? This is where Lambda Expressions kind of plays a nice and tactical sugar here.
40:02
So what is the method I'm interested in? Well, the method I'm interested in, get rate for vendor, remember? That's the method that the class has. But remember in C sharp, we can have function overloading. So that could be multiple methods potentially with the same name, and how do you tell him which of those methods you are interested?
40:21
Well, you know that while multiple methods can have the same name, their signatures have to be different. So a method name combined with the signature makes the name unique. So that is why, notice over here, the name of the method is get rate vendor string string because it accepts two strings as parameters.
40:41
So we simply give him a Lambda expression. Here we say vendor name and currency, and all that I want him to return is a value of 100 from this. And we simply told him, just return back a value of 100. So that is our mock object. We just instantiated right here within this test. And that is all we needed to do.
41:01
Let's go ahead and run this test real quick. And notice the test is still passing at this point. But let's go ahead and remove this mock class now because we no longer need it. In fact, while I'm removing this mock class, I'm going to go ahead and remove the other mock class as well. So we got rid of both the mock classes we created so we don't have the burden of creating extra classes anymore.
41:23
But instead what we are going to do now is to go to the second test at this point. And right there is the mock vendor service that blows up sitting here. And instead of doing this again, we're going to simply say here one more time, this is going to be the currency exchange library
41:40
dot fakes dot stub for service. And we will simply ask him to create an instance again. And in this case, the instance we're going to create over here is going to take the get rate exchange. And what does this do, by the way? Well, it's going to take a vendor name and a service currency again. But I want this to blow up.
42:01
In order to do this, you put a little curly bracket because you're not just returning a value at this point and simply say throw new application exception. And this is going to simply throw with a simulated exception as a value that I wanted to throw back at us. So that is the exception we're going to throw from this little method itself.
42:22
And this is going to be the simulated exception that I want to throw back from here. So let's make sure this is working at this point and we can see all of our tests are still passing. So we did a fairly lightweight mocking, but we used a fakes library. So what's going on at this point, by the way? The fakes library essentially is creating a stub for us automatically.
42:42
It creates this at compile time. And so we are simply setting up this by entertaining a few properties on this. So the get rate for vendor string string is actually a property on this class, a public property on the class, and you're assigning a lambda expression to it. And that's how you're saying this is what I want you to do.
43:01
So when we make the method call, the call then calls the service, which really is the mock that we implemented. Fakes intercepts that and says, here you go, I'm giving you a value of 100, or throwing an exception where it makes sense. Does it make sense so far? So what is the benefit so far? We don't spend time creating mock classes. We simply set up our mock and use it.
43:22
Well, that's great so far in this case, but let's get to a little bit more complex situation. What if I have two vendors, by the way? Well, if I have two vendors, I want to be able to handle multiple vendors here, obviously, but how would I set up the mock for that? So here is my test for that. I'll say rate when two vendors given,
43:44
and I'll just write one test here instead of two tests with a high rate, by the way. So I'm going to say second vendor giving the high rate. So currency exchange over here is going to simply say a new currency exchange. We've got to give them a vendor service.
44:00
So we'll say vendor service, and I'm going to set up the service in just a second. We'll come back to that. So what am I going to do with this vendor service itself? Well, I'm going to ask him again what is the expected value. So expected equals new tuple, once more, string and double. I want this to return a vendor two, by the way,
44:20
and I want this value to be, let's say, a 48 is the value I would expect from here. And then, of course, in this case, let's go ahead and say I want to call result equals, we'll call it rate equals, currency exchange dot get rate, and, of course, let's give him the currency name and assert equals, and this would be, of course, expected,
44:43
and then rate itself. But for this to work, I've got to set up the mock properly. But before we do this, we have to tell him the vendors as well. So currency exchange dot set vendors, this time I'm going to give him a vendor one and a vendor two as well. Now, for this to work, obviously,
45:01
we have to make a little bit of effort here. So to do this, let's go back to the code, and we will tell the set vendors to take a collection of vendors itself. So we'll say params over here, and this be vendors rather than vendor, by the way, so a little small refactoring code, and this will become, of course, a vendors collection
45:22
that we have to deal with. So we'll just simply say vendors over here, which is going to be a collection. Let's just initialize it to a new string of empty vendors. So let's go ahead and say that's the value that I'm interested in creating to begin with. All right, that is good so far. Now, what am I going to do with the vendors collection itself?
45:40
I've got to set that up in the mock object. So let's go ahead and do that. So here is the vendor service. So vendor service equals, and in this case, of course, I've got to tell the mock one more time. So let's go back to creating the currency exchange library, and we will tell him to create a stub. But this one is going to do a little bit different
46:02
than what we did before. So here is my get rate for string string equals, and here is the vendor again in currency, and all that I'm going to do simply is return from here, let's call it rates, and then a vendor. So rates is basically a little map I'm going to create right here.
46:24
So let's go ahead and say bar rates equals new dictionary, and this is going to be a dictionary of string and double, and this is going to simply accept a bunch of values for us to use, and the value that I'm going to tell him to use is going to be a vendor one,
46:42
so let's say vendor one, and rate is going to be, let's say, a 10, and it's going to be also a vendor two, so let's go ahead and give him a second value for this mock. So this is vendor two, and let's go ahead and tell him the value is 50 in this case that I want him to use. So let's make the compiler happy right here.
47:01
So let's go ahead and use a using for it. So I think we are about ready to try this example. But for this to work, obviously, our code has to use multiple vendors, so we'll get back to the code. Where do we deal with it? Well, rather than saying use the if statement, we'll quickly convert it to a for each statement,
47:20
and then we will say here for a vendor in, this is going to be vendors itself. So we're going to go through each of the vendors here, and if something fails, of course, we want to log it, but we haven't gotten to that yet. We'll come back to that in the next example. So what am I going to do at this point? So in this case, of course, this is going to be a vendor we are interested in talking to,
47:40
and if it is true, it is going to use the vendor's name. Otherwise, we're going to log the exception and move on. So assuming that we did all that right, we can try to run the test, and we can see that the tests are again passing, so we implemented and refactored the code to use multiple vendors. Let's kind of up this a notch now. I'm going to skip these three vendors for now,
48:01
but I want to deal with an error condition. What if a vendor fails? We already saw that. We have a way to handle the error, but I want to log a message. How do I log a message when something goes wrong? Well, let's think about this for a minute, again, by writing a test case. So here is my test for logging a message. So back over here in the test class,
48:21
let's go ahead and write a little test that says rate when log message, let's say log message when a service fails. So how do I write a test for it, first of all? We've got to decide where we want to store it. Maybe I want to store my error messages in an errors.log file.
48:43
So let's say assert are equal, and I want errors.log, and this is where my error file is going to be, and that's where I want to store it. So I'm thinking about the expectation first, and then we'll fill in the code. What is the message I want to store, by the way? So assert are equal one more time,
49:02
and what's the message? The message I want to get is going to be this message. Let's go ahead and say the message is error talking to service for vendor one, and then on, and I want to put a date over here, but I don't know what date to put yet,
49:21
so I'll just put a blank for now, and then the error message is going to be simulated exception. So this is what I want the exception message to be, error talking to service for vendor one on that date, and the error message is simulated exception. Let's see how we can make this work. So to make this work, I'm going to first of all tell my currency exchange
49:42
that I do have a vendor to work with. So this is going to be the code for it. So I'm going to simply tell him that I only have one vendor, vendor one, because that's all I care about right now. But I have to tell him to blow up on that time. That's very easy to do. So I'm going to say currency exchange dot get rate,
50:01
and here is the rate I provide for him, and what about these messages, by the way? So let's go ahead and say bar error message, or error file equals empty, and bar message equals empty as well. So those two are empty to begin with. Let's go ahead and set the mark, by the way, so that it fails to return back the value properly
50:24
when the mark tells us to fail. So I vendor service. So here is a mark we already created. We can reuse it with a simulated exception. So I'm going to grab this part right here. So let's go ahead and move that. You can always refactor these tests to reuse some of those parts, by the way,
50:41
so you don't have to duplicate it so many times. So this guy blows up, as you can see. Let's go ahead and run the test and see what it does. So when I run this test in this case, it should tell us that, oh, wait a minute, this is saying when the two is broken, so let's go ahead and see what this was doing, first of all. Maybe I was not noticing that a few minutes ago. So what's this failing with when the two vendors are given?
51:03
So in this case, of course, pardon me, so since the other test is failing, I should worry about that first. So get rid of this for a minute. We'll come back to this. So let's go back to that test. There we go. So let's see why that test is failing. I didn't pay attention as I was speaking. So let's go ahead and see what error he gives us here.
51:23
He says, aha, the value is 48 instead of 49. Venkat cannot do math on Friday evenings. 49 is 2% down from 50, isn't it? Yeah, thank you. So that's basically the reason why that failed. Let's make sure that's working great. So let's get back to this test here. So what does this do, by the way?
51:42
This says I want to go ahead and create a mock that blows up, and once it blows up, I want to call the get rate, and the get rate should tell me that it's logged the error to error file, and that's the message it logged. But how would I do this? When I run this, it fails. What's the reason for the failure? It tells me the error message was file name was empty,
52:03
whereas I told him that the file name would be called errors.log. He says, what are you talking about? That didn't happen. So let's think about how we're going to implement this. I'm going to go to my get rate method. When there is a failure, my get rate method is going to say, hey, go ahead and send this to a file for logging the error.
52:23
So I'm going to talk to a file. And what do I do with the file? Let's keep this very simple. Typically, you would use some kind of a logging library. That's fine. You can still mock that out here as well. But I'm going to just write to a file. So what am I going to write to in this case? Well, I'm going to write to a file object, but where do I get the file object to mock from?
52:42
Notice in the previous example what we did. To mock this out, we created a mock object, and then we sent it to a constructor injection. We said you could do a constructor injection or a setter method injection, but none of those will work in this case because the fact that I want to use a file is my internal business. Now, typically, when you read about test-driven development,
53:03
people will tell you, yeah, if that's really important, expose that as an interface so we can send it. Yes, you can do that, but that causes unnecessary complexity. I don't want to go that route. Well, it turns out Fakes actually has a way, somewhat of a peculiar approach,
53:21
but it really is an AOP approach, an Asperton-to-Programming approach. It gives you a way to set up what are called shims. Shims are a lot more powerful than stubs, but they're also more expensive at run time, so be very careful using them. But what a shim is, it can really intercept your call quietly right in the middle and say, rather than going over there,
53:42
hijack the call to this particular implementation. So it's purely an Asperton-oriented implementation where you can intercept calls very quietly and take them elsewhere without even the code knowing that you're doing this. So notice what I'm going to do here to make this work. So the first thing I'm going to do here is,
54:00
right where we are calling this method, I'm going to go around this method to do this job. So I'm going to first of all go back to this code, but to run this, you need a context to run it in. So I'm going to create a using block right there, and within this using block, I'm going to say a shims context dot,
54:21
and I'm going to call a create method. So create. So what is this shims context? Well, shims context gives you the context of execution for the shim. It comes from the testing dot fix namespace, as you can see here. So we created a method that is going to bring it in, and right within this, I'm going to call this method get rate.
54:42
So this gives a context of execution where it is going to start intercepting things. But what am I going to really intercept here, by the way? So I know where files is located. It is in the system library. So I'm going to select it and say add fakes assembly for system. So we brought in the fakes assembly for system,
55:00
and now I'm going to say here the system dot fakes, IO dot fakes over here. So I'm going to bring in fakes. So let's make sure the compiler compiled it. So let's go ahead and make sure this compilation works. So I'm going to go ahead and ask him to bring in the system dot IO dot fakes over here dot file.
55:25
So I'm going to bring in a shim for a file, and then I'm going to say dot, and I'm going to write all text. And notice, very similar to what we did before, I'm hijacking the write all text method. And what is this method going to take, by the way?
55:42
A file name as a parameter and an error message as a parameter. And all that I'm going to do within this method, by the way, is simply to write in here. I'm going to simply say go ahead and set the error file name to file name for me and the message to error message for me.
56:03
So I grab these two values and I just set it. That's all I did. The test won't still work, obviously, because we haven't changed the code to do it yet. But notice what I'm going to do now. I'm going to go into the get files method, and I'm going to tell him to use this file to write to it. So let's get back here to the file and say,
56:20
when there is an exception, by the way, go ahead and go to the file class, by the way. And in the file class, go ahead and write all text. So write all text. And where am I going to write, by the way? To the errors dot log. And what am I going to write? A message I want to write. Well, what is this message I want to write?
56:42
Message is going to be a string dot format. And what is this format I want to write at this time? Well, the format I want to write here is going to be this message with a few values embedded in it. But let's figure out how to write this. So let's grab this for a second. Let's get back over here and say, here is the message I want you to write.
57:02
In which case, error talking to service for, well, this is a value I want to give. On, this is the value I want to give. So let's just say a one for it. And what is the error message? That is the value I want to give. So let's go ahead and replace that with a two. So let's go ahead and say what these values are. The first one is a vendor name.
57:22
The second one is a time, which I don't know yet. I'll put an empty for it. And the third one I want to give is the exception dot message itself. In this case, whatever went wrong, I want to give that message. Let's go ahead and run this test and see what it does. And notice the test is passing now. So what did we just do?
57:40
What we did was, in the test we said, given this particular code, go ahead and simulate away and mark away the write all text string method itself from the object, right? So we are sitting outside here and kind of telling him what to do deep down here.
58:01
So it's a very powerful dependency injection without being intrusive. Very powerful, but be very careful using it. Don't use this for all purposes. So then within this code, without knowing any of this happening, we quietly write to this file. One last thing I want to fix here, by the way, before we go. I want to come down and say, what is the date, by the way?
58:22
Well, obviously what I want to do is I want to display the date and time when this happened. But there is a problem, right? If I say date time now, whenever the test is running, it's going to find the time. How do I know what the value should be here? Hey, why don't you find the date, time, and send it? You're still working on this microsecond differences
58:40
and test start failing at times, which becomes really annoying. So to fix that problem, what we're going to do here, we'll simply say here 6-14-2013, and then let's just say 8 a.m. So 8-0000 a.m. That's all I put. So I put a random time right here, and I said that's the time failure I want to have.
59:02
Well, of course, I go to the code now, and I'm going to say here simply date time.now, and I'm going to run the test. When I run the test, notice what happens. The test is going to fail, but the message of failure says, hey, you expected at 8-14-2013, 8 a.m.,
59:21
but what I really got was 6-14-2013, 3-59-22 p.m. Well, let's fix that real quickly. I can go back to the test message itself and say, well, this is going to be 6-14, by the way, but that still fails because, of course, I removed the zero. What about the other value?
59:40
No worries. We can easily fix that. So I go back over here and say system over here, dot, date time, and then dot, actually for the fakes, right, dot fakes, and then I bring in the date time shim, but this one, by the way, is on the instance over here.
01:00:00
So, I'm going to say getNow, and then I'm going to ask him to return for us a new dateTime with whatever value we want to simulate. In this case, I'm going to say, what are the values that the dateTime takes, by the way? Well, it is taking a value of year, so 2013, month is 6, the day is 14, 8 a.m., so give those values,
01:00:26
and then those are the values I want him to return, and go ahead and run the test, and now we faked away the dateTime object itself. So, the code, when it comes and calls dateTime.now, it is not returning the real dateTime anymore,
01:00:42
but the simulated dateTime we created up in the fakes over here. So, this gives a fairly powerful way to reach in and mock, but like I said, be very careful using it, don't do this for everything you do, and this can also be a little slow in computing, but this really gives a way for us to deal with the dependencies. Once we finish this, of course, we can now step back and start writing the class for implementing the interface we wrote,
01:01:07
and to do that, by the way, we can approach very similar way, and you don't have to really force us to write the real code until your test pass and gradually you can get to the point. So, if you're interested in looking at the code to talk to the web service,
01:01:21
feel free to download the code from my website, and you can download the code and play with it. It's a fairly simple concept, but it's more of an approach to follow, and the tools definitely do help what we are trying to do here. So, test driven development is extremely approachable, and I've looked at a pretty gnarly code to write tests for,
01:01:40
but I haven't seen a problem so far that I say, darn it, this code is untestable. It always has been a design issue, and once we kind of figure out how to reach in, tools do help, but tools are not the only solution. It's a combination of approach we take and the tools we apply that makes a big difference. I hope you found that useful. Thank you.