C# Interactive Initialisation Trick

Hi there,

A little aside this one; something very, very cool that I’ve noticed with the advent of Visual Studio Update 2. Without question, this rates as a MUST CHECK OUT Visual Studio feature.

The first complaint (and a valid one, no question about it) in regards to the C# Interactive Window was that running your own code was a little bit of a painstaking process, requiring you to load dlls and supporting references manually, using the #r command. Now, with update 2, initialising C# Interactive with your applications context is a cinch.

For starters, right-click on the project you are interested in and select the Initialize Interactive with Project option, within the Solution Explorer:

Showing how to Initialise Interactive with Project.

Initialise Interactive with Project Option.

This, as you’ll see, does all of the ‘scaffolding’ work for you within the C# Interactive Window, and injects a using statement for the default namespace to get you started. You should see something like the following:

C# Interactive Window initialised so that the Project is in scope.

C# Interactive Window Initialised with Project.

From here on in, you are free to start adding using statements, as required, and begin writing code against the types in the target project; in a completely pain-free fashion. A massive thumbs up…you can now go wild, like this for example:

Example of using Project Code from C# Interactive.

Using Project Code from C# Interactive.

I did try this on a larger, more complex and tightly-coupled code base and I can comfortably say you’ll have issues depending on how things are structured. Smaller, more loosely-coupled types, where SOLID principals have been adhered to are going to allow you to use C# Interactive in this way with more ease.

It is also possible to highlight portions of code, in any file, and right-click the Execute in Interactive option to run with, and execute, smaller chunks of functionality.

Posts on the way to cover some elements of the ASP.NET Web API and Ninject Dependency Injection (hopefully, at some point over the weekend), so watch this space.

Have a good weekend all and I’ll be back with you in a jiffy…

Developer Testing Hints and Tips

Howdy happy campers.

I want to discuss a piece, somewhat divergent from the topic of physical coding, although still a facet of development that is close to my heart (and easy to overlook in many respects when constantly mashing keys and churning out code); developer testing. More specifically, I want to provide a set of guidelines that ‘may’ (insert disclaimer) help with the process and provide some food for thought.

This is in no way a definitive guide or best practice for that matter; more just a personal take on what I find works for me and the guts of a generally beneficial ‘templated’ approach to follow.

I would love to invite discussion on this one (or just get a take on what works for you), so please do hit me up on twitter or add a comment below, I’d love to hear from you.

My Process

As with any process, ground work and preparation can be vital for achieving a good result. To this end, I invariably start my developer testing on a given work item with a template document that looks like this:

Illustration of how to structure you Developer Testing.

Developer Testing Helper Document Structure.

What goes into your document will largely depend on what technologies you are using of course. For instance, you may never have a database centric element to the development you perform, rendering the ‘Database Upgrade’ section null and void ‘in all cases’. Ultimately, add and remove sections as you see fit but do strive for consistency. I myself test a mixture of items that may or may not include T-SQL elements. However, I choose to include the ‘Database Upgrade’ section in this case on every occasion, preferring to note that ‘there were no T-SQL’ related parts to the item, even just to mark it as ‘N/A’ (for my own sanity and for easy recollection later down the line, without the need to scan a lengthy list of changes elsewhere in the notes). Basically, my OCD kicks in and I start to wonder why I haven’t included a section that I ‘always’ include, leading to paranoia that I’ve missed something!

Each section (other than Notes), which is probably self-explanatory, can result in a PASS, QUERY or PASS-BACK state. Section state obviously knocks on and influences the result recorded against the ‘Developer Testing Summary’ header. PASS denotes an ‘A-Okay’ state, good to rock and roll! QUERY gives you the opportunity to mark a section with ‘discussion points’ or things you would like to check, without necessarily marking it off as incorrect (I tend to do this a lot, as I love to talk!). PASS-BACK is used in the circumstance whereby an error can be replicated/reproduced consistently or a logic problem definitely flies in the face of the ‘Acceptance Criteria’ for the story. In the circumstances whereby things such as coding standards have been contradicted I tend to use a mixture of QUERY/PASS-BACK, depending on the notes the developer has provided (it could be a flat PASS, of course, as there are always occasions where the rules need to be broken!).

So, section by section, let’s go over what we have and why…

Notes

It’s incredibly tempting to start diving into code, comparing files, trying to make sense of what the hell is going on but…I may get in trouble here, I’m going to tell you to stop right here. It’s so easy, and I’ve done it (probably) hundreds of times, to get eye deep in code, wasting large pots of time, before the basic question of ‘what are we doing and why’ has been answered. This is where this section comes in.

Use this area of your notes to compile a few short paragraphs (or bullet points, whatever you prefer) on the following:

  • Read over the developers notes and, after discovering if any changes have occurred to the underlying requirements for the story, start to create…
  • Your own summary of the ‘Acceptance Criteria’ for this particular story (or item, whatever term floats your boat. I’m going to use both interchangeably to alleviate bombarding you with the same term too much!).
  • Then, list any other pertinent information surrounding how the developer has coded the item (e.g. decisions that have shaped how the story has turned out). For example, did they place code into a different service than was originally expected because of ‘x’ reason, or did some logic end up in a different layer in the technology stack than conceived originally.
  • Lastly, note any of your initial thoughts, concerns or things you intend to check/look for based on this initial scoop of information.

The core reason I do this is to try to solidify my expectations, begin thinking about a test plan (yes, I like to always perform (rudimentary at the bare minimum) application testing, this isn’t just down to QA in my mind!) and to try to mitigate the chances of any massive surprises. Surprises, although they will always eventually happen one way or another, will lead to more confusion and increase the chances of things slipping through the net. You’ll be able to, by just following this exercise or a similar routine, cross-reference your expectations with the code changes you see and more easily be able to pick up errors, incorrect logic or unrequired alterations. This will limit the chances that something will slip past your mental filter as an ‘I guess that’s correct’ or ‘perhaps that class needed to be changed also, ok’ moment (don’t lie, we’ve all had them 😉 !).

Cool, we’ve formed in our own minds what this item is for, how it’s been developed and what, as a baseline, we are expecting to see. Let’s test (and along the way, discuss a few more tactics).

Database Upgrade

Some of what I’ll discuss here is formed around how my personal development role operates, so feel free to modify this approach to your needs. Again, if you don’t deal in the realm of database development at all pass go and collect £200, you’ve bypassed this step; congratulations!

The essence of this section surrounds you being able to state that new Stored Procedures, Functions, Views, Triggers, etc. can be ‘created’ without error on a database in a suitable ‘versioned’ state. Also, can ad-hoc data scripts, that are part of the development item, be run without error?

Some other considerations…

  • Are object creation scripts/ad-hoc scripts expected to be re-runnable? If yes, then specifically test and note this down here.
  • If you are in an environment whereby this kind of testing needs to be performed on multiple databases then mark this down here also (splitting notes down into sections against each target database/environment, whatever is applicable).
  • We work with a ‘versioned’ database so I make an effort to state which version I am on at the start of the testing run for reference.

An example of what this section may look like is illustrated below for reference:

Illustration of how to structure the Database Upgrade Developer Testing Document Section.

Developer Testing Database Upgrade Section Example.

A QUERY/PASS-BACK at this stage will bubble up and alter the status listed for the entire developer testing process. An additional note here; depending on how many queries/issues you find (and the length of the testing notes in general), you may want to copy the core query/error text to the top of the notes for easy review by the developer later (this applies to all of the following sections in fact).

Code Review

Moving on to the main filling of your developer testing sandwich, the actual code review! Obviously, you’ll be reviewing files here and looking at scripts, new files or amended code but definitely take a second or two out (unless your setup has automated builds/continuous integration, or some other clever solution, to tell you this) to make sure the code compiles before proceeding (and make the relevant note). A simple step but one easily forgotten, meaning you can get to the end of a code review before realising parts of the code don’t compile, eek!

I tend to, from a structural and sanity point of view (clarity is key), split my testing notes here into sections based on technology (i.e. T-SQL, C#, JavaScript, etc), or, at least, make some effort to order up a single list of files by file type. I tend to, for C# changes, group code files by the related project (given that projects should represent a logical grouping of types, hence allowing you to dice up changes by functional area, i.e. common extensions, data access helpers, etc.).

The point that you should take away from this, however, is that a little bit of thought and structuring at this phase will make your life easier; especially as a number of code files rack up.

If you’re looking for a small sample on how this section could look, after being fleshed out, then here you go:

Illustration of how to structure the Code Review Developer Testing Document Section.

Developer Testing Code Review Section Example.

However, what about the code review procedure itself I hear you cry! What follows next shouldn’t be taken as an exhaustive list, or correct in every given situation for that matter; more just suggestions as to what I’ve found helpful over time (mental kit bag):

  • For C# (and other object-orientated languages that support this concept), ensure that null values are correctly handled. Whether this is by capturing nulls on call to a given method and throwing an ArgumentNullException, or by doing a ‘not equal to null’ check (!= null) around code that would otherwise fail.
  • Strings can be tricky buggers, especially in case-sensitive environments! In most cases, comparisons should be performed taking case-sensitivity out of the equation (another case-by-case situation of course). I’d keep an eye out, again for C#, for the correct use of String.ToUpperInvariant, String.ToLowerInvariant and String.Equals. For String.Equals, use an overload containing a StringComparison enumeration type, for case/culture-insensitive options.
  • Keep an eye out for instances of checks being performed against strings being null or an empty string (either one or the other only). This can quickly lead to chaos, switch out for a null, empty or whitespace check (e.g. String.IsNullOrWhiteSpace).
  • Empty try/catch handlers are evil. Kill any you find.
  • Check up for instances whereby a class consists of all static members, but the class is not marked as static.
  • Train the eye to look for casting operations; you’ll always catch a few where the casting operation ‘could’ throw exceptions and should, therefore, be subject to more careful handling.
  • Big bugbear in the realm of coding; if a method requires scrolling to get through it’s a significant indication right off the bat that it is a prime candidate for refactoring. Unless there is a good reason, or it is clearly performing one logical function, consider having a conversation about breaking the method down.
  • Look for missed opportunities to rational code using inheritance. The most common one I see (and forget myself) is the abstraction of code to base classes and then using virtual methods/overrides in subclasses. Hawk-eye for types that should be abstract.
  • A simple one, but something that could easily slap you in the face if you’re not careful. When ‘language switching’, in a DT sense, take a second to make a mental note that you should be changing mind-sets (i.e. syntax is changing, get your game-face on!). For example, you stare into the abyss of C# for long enough (seeing ‘!= null’) you may, on switching to T-SQL, not notice a ‘!= NULL’ that should have been an ‘IS NOT NULL’. Those trees can be damn hard to find in the woods, after all!
  • Watch out for expensive operations, whereby values should be obtained once, ideally, then cached. It can be easy to let code skip by that repeatedly calls a database, for instance, to the detriment of performance (or possible errors, depending on the nature of the functionality called).
  • I love, love, loooovvvveeeee comments! Probably (ok, to the levels of being a little OCD about it!) too much. As far as C# code goes, I prefer (but don’t fail on this basis alone) XML Comments for C# and like to see comments on bulkier pieces of T-SQL. If there is a sizeable piece of code, whereby its function stretches beyond ‘trivial’, I like to see at least a short statement stating intent (what the developer is expecting the code to do is key by the way…as discussed next).
  • Where you have comments, link the intent in these comments back to what the code is actually doing; then trail it back to the items ‘Acceptance Criteria’ where appropriate. I have been rescued (as a developer, submitting my work for DT) countless times by those performing DT on my code, just by someone relaying to me that ‘what I thought my code was doing’ (based on my comments) doesn’t tie up to the actual functionality being offered up. This has led to me, of course, face-palming myself but being relieved that the gap in my intent, when checked off against my actual code, had been picked up by somebody in time to catch it before QA (or deployment, gulp!). State intent, then reap the rewards when mistakes you make are more rapidly picked up for rectification.
  • Be sure to look for the use of language constructs/keywords or syntactic-sugar that is not permissible on your baseline, minimum supported environment (i.e. older versions of SQL Server or .NET), if what you work on has this concept of course. This is sure to be something that will get picked up by QA causing bounce backs, or by your consumers later on if you’re not careful!
  • Keep a look out for code that could (or should) be shared or has been placed in a project/location that does not make logical sense. At a bare minimum, picking up on this sooner rather than later will keep your code base tidier, allow for ample opportunities to put great code in places to be leveraged as much as possible. In other cases, asking these kinds of questions can expose flaws and issues with the way a solution has been architected, which occasionally will steer you clear of tight spots later down the line.
  • Where shared code has been changed, look for instances whereby other applications/areas of the code base could be broken as a result of the changes. Recompile code to check for this as required. I had a bite on the bum by this recently :-?.
  • Keep up to date with any coding standards documents that should be adhered to and make sure the guidelines are followed (within reason of course; you’ll always find a scenario whereby a rule can, and should, be broken).
  • Really do consider writing and using Unit Tests wherever possible. They are a useful facet in the grand scheme of things (I believe at least) and they do carry weight when pitched up against visually checking code and application testing in general.
  • Last little nuggets, which I see from time to time. Look for objects constantly being created inside loops, heavy amounts of string concatenation not using the correct constructs (e.g. a StringBuilder in C#) or missed opportunities to create sub Stored Procedures in T-SQL (sectioning off code to gain performance boosts and obtain better execution plans). In fact, for T-SQL it can be a useful exercise to check the performance of non-trivial pieces of code yourself by changing how it’s structured, whilst obtaining the same results of course. You may or may not be able to increase performance along the way, but you’ll have far better comprehension of the code by the end regardless.

Hopefully, this little snapshot from my bag o’ tricks is enough to get you started, or get the brain-juices flowing. Let me know what you think of these suggestions anyway; I’d really appreciate the opportunity to collate others general thoughts and get a collective consensus going.

Application Testing

Here is where I will defer the giving of advice to my beloved QA counterparts on this beautiful planet; this, of course, isn’t my area of expertise. My only opinion here is (developers will possibly hate me for stating it) that developers ‘should’ always perform application testing alongside a code review. In fact, I’m a keen advocate for developers being involved in performing QA on the odd occasion. I personally like doing this, provided I have a trusty QA on hand to assist me (thankfully, I work with the best one around ;-), so no worries there). The simple reasons for this are:

  • One way or the other, acquisition of Product Knowledge is going to be invaluable to you. It’s just as valuable to start using your products in anger as it is to analyse code for hours on end. The side-note here is that this is part of your overall ‘worth’ as a developer, so don’t neglect it.
  • At this stage, you get to think as the customer might. Ideas and thoughts you have at this stage, which direct more development or changes to the product, will be amongst some of the best (and most rewarding when it comes to getting that warm and fuzzy feeling!).
  • Urm…it’s embarrassing to say ‘oh yeah, that codes great, thumbs up!’ for it then to explode in someone else’s face on the first press of a button! Easily avoided by following the process through from end to end, no matter what.

Ok, I’ll have a go at channelling one QA thought. Ok, I got it, here’s one from a mysterious and wise QA guru:

Mysterious and wise guru here… a friendly reminder to developers…never, ever, test your items using only one record! The reason? Well, I’ll test it with more than one record and break it instantly!

If anyone doing QA reads this feel free to feed us your arcane knowledge…God knows we need it! I would advise you keep the original item requirements in mind here of course, whilst testing; securing any process variants in your thoughts that could potentially throw carefully laid plans to waste (e.g. what if we go back and forth from screens x and y between completing process z, or we save the same form information twice, etc.). Your knowledge of the code can help at this stage so use the opportunity whilst you have it.

Before I forgot, an example of this could look like this:

Illustration of how to structure the Application Testing Developer Testing Document Section.

Developer Testing Application Testing Section Example.

Code Review/Application Testing – The Most Important Point…

Do it!!! If you’re not sure (as I am still on a regular basis) then ask the question and run the risk of looking like an idiot! Be a spanner, who cares at the end of the day. I dread to think of how many developers have stared at code and, ultimately, let stuff slide because they refused to pipe up and just say they weren’t sure or ‘didn’t get it’. At the end of the day, it’s better to ask questions and if there turns out to be no issues, or it’s a simple misunderstanding, then no harm, no foul. On a good number of occasions I query things to later realise that I missed a line of code meaning it does work as intended, or there’s some process that had slipped my mind…it hasn’t got me sacked (ahem, yet!). So my advice is just to open up and have a natter at the end of day, it’ll be worth the ratio of ‘idiot’ to ‘bug-saving’ moments, trust me :-).

Admin

As with any process, there will always be (and if there isn’t for you then let me know where you work because it’s awesome!) a certain amount of ‘red tape’. Use this last section to keep track of whether any procedural bits and bobs have been handled. For example, I’m expected to cover the creation of a Release Note (as part of the practices I follow) for any item I work on, so it should be marked down in this section as to whether I’ve completed it or not. It could end up just being a very simple section, like the following:

Illustration of how to structure the Admin Developer Testing Document Section.

Developer Testing Admin Section Example.

I hope this has been helpful and informative; or, at least, got the mind going to start thinking about this process. Again, as mentioned above, I would love to hear your thoughts so please do get in touch either here or via social media.

Cheers all, keep smacking keys and producing coding loveliness in the meantime 🙂

Codecademy.com – JavaScript Course Impressions

In my day to day job we use JavaScript in anger (or at least have to deal with it) on a sporadic basis. We dive into T-SQL, C# (either for server side logic in web applications or for desktop applications) and other parts of web development (CSS and HTML) on a rapidly spinning merry-go-round of coding fun. The variation is great but getting some solid exposure time is also nice to have. JavaScript in particular, as a pillar of web development (alongside HTML and CSS), is something that I’ve wanted to spend more time on (even though I have a solid grounding now); so I’ve decided to slowly edge my way towards a Microsoft Certification. Namely, the MCSD: Web Applications Certification.

This ties in nicely to a personal goal of mine to test out codecademy.com; just to take it for a spin more than anything else. So, I’ve signed up in order to run through the beginner JavaScript course; just to give my impressions on it (and to see if anything can be gleaned – Start with the basics and work on up!).

Although the content is basic, I actually found it pretty well laid out; I can imagine somebody new to coding finding this fairly easy to follow which is fantastic. The interface is clean and simple.

The basics of throwing dialogs are covered early on (confirm and prompt – I haven’t seen alert yet however), along with a simple discussion on datatypes (numbers, strings and booleans as you’d expected). The classic debugging aid that is console.log is also discussed (always a bit of a go to I find!).

Beyond simple mathematical operations and string manipulation techniques I came across the first thing, that is always on the periphery of my mind and can easily catch you unawares (that differs slightly to the C# world so all the more easy to get it wrong); the equality operators. In order to compare that something is of equal value and equal type ‘!==’ and ‘===’ must be used, a subtlety worth remembering and easy to trip up on when the old noggin’ is not switched on.

Comparison Operator Differences.

Comparison Operator Differences.

The full w3schools documentation relating to this can be found here:

JavaScript Comparison Operators

Also, I tried to throw the site out a few times by leaving some humdinger code in place; all handled well in every instance:

Does It Catch Mistakes?

Does It Catch Mistakes?

Does Not Compute!

Does Not Compute!

I did, in some areas, go out of my way to code the examples slightly differently to how it was specified (looking to get the same result of course in all cases) and, for the most part, it seemed quite resilient to this (including the use of functions not specifically mentioned in the examples).

The little section regarding comments was a joy to behold. I’m well known for writing a tonne of comments; pretty sure everyone hates trawling through them (what can I say, it’s a habit!). This is certainly what comments often boil down to however:

What Code Comments Are Really About!

What Code Comments Are Really About!

You also get offered up the classic programming golden nugget; the piece of knowledge no one can do without – The origin of the term debugging! Always a classic!

Lastly, I actually quite like working with zero intellisense. It’s always useful to get that little nudge in the back; a prod to say ‘hey you, switch on and get your act in gear. I’m not going to auto-complete everything for you and let you tab your way to glory!’. I’ve actually sat down in the past and done rudimentary C# programming and very basic websites using notepad alone; well worth doing in my opinion.

It’s been an interesting side-line at any rate so I’ll continue on for now. I’ll plan to move on to the w3schools Certification as a middle ground, before MCSD Certification, so more to follow shortly.

Cheerio!

Visual Studio 2015 First Impressions – PerfTips

I’ve had a super quick tour of Visual Studio 2015 Community Edition and I have to say I’m pretty impressed as it stands. I’m currently downloading Unity 5 and will be installing the latest toolkits shortly that go hand in hand with this.

In the meantime, I’ve been looking at some of the new debugging performance aids; namely, PerfTips.

Full information can be found here:

Visual Studio PerfTips

In the example screenshot below I have rigged some methods (using the new async syntax in a rough and ready way) to simulate a slight delay in the retrieval of information. I’ve placed breakpoints in the code and you can see that, when hit, a small diagnostic message is shown in the editor detailing the time, in milliseconds, that have elapsed during the previous method call. Very, very nice:

PerfTips In Action

PerfTips In Action

You will also notice that some extra diagnostics, listing the events that have occurred so far and interesting metrics such as memory usage, are being shown in the Diagnostic Tools window on the far right. This is something I’ll have a further dig into; looks interesting on first glance.

The only thing I didn’t like off the bat was the default colour of the PerfTip text itself (which is non-descript when not highlighted, as standard). I have a penchant for the Visual Studio Dark theme so I prefer colours to pop a little more, just personal taste really. Thankfully, this can be easily rectified by navigating to Tools > Options > Environment > Fonts and ‘Colours’ (sorry, I’m British, I can’t help myself!) as shown here:

PerfTips Before Customisation.

PerfTips Before Customisation.

From here, you can adjust the Item foreground value (ensuring that PerfTips is selected from the Show Settings for drop down and that Text is highlighted in the Display items selection box). Of course, you’re also free to set the colour of the PerfTip text when highlighted as well by selecting the Highlighted Text item from the Display Items selection box and adjusting the Item foreground to the desired colour. If the colours on offer don’t float your boat then the usual custom colour picker is present also.

You can fly by this handy, dandy post for more details:

Customise PerfTips

The end result (depending on the colour you pick) is as follows:

PerfTips After Customisation.

PerfTips After Customisation.

I did receive a message stating that the application may need a restart for changes to take hold. In this case, I got away with it and the change kicked in immediately.

Anyway, just a short post for tonight. I will detail any other interesting finds as they crop up.

Until the next time!

Decompiling/Recompiling to fix a real issue – It’s been a while!

I won’t be using ‘real world’ coding samples here, for obvious reasons, but I’ll lay out a scenario so you can (hopefully!) see how a spot of decompiling/recompiling could come in handy.

An issue rolled its way to me that related to a legacy/archaic .Net website utilising a WCF service.

First issue, important parts of the source code were not available to me. I’m sure you’ll agree, this is not a great position to be in considering we had only just reached the starting block!

After a small amount of toiling, I hit the inevitable point whereby I’d solved any IIS/WCF configuration issues and now, desperately, needed to get into the meat of the source code. The only option was to extract the dlls that made up the application and supporting services and get decompiling!

There are some great tools for this. If you want to pay money and want a nice little selection of addition perks, then Redgates .Net Reflector is still a great pick:

Redgate .Net Reflector

On the free end of the spectrum I prefer (and this certainly seems to be a common pick amongst my colleagues) Teleriks JustDecompile:

Telerik JustDecompile

Armed with the dll dump and JustDecompile I went to work. Logical steps to enable basic logging at the service level had solved a handful of issues which is all fine and dandy. Unfortunately, I hit a dead end when the application surfaced an ‘unknown error’ in the UI and there was no sniff of an exception in the trace logs; bloody dark days if you ask me!

I took the offending dlls and started digging. A situation similar to the following presented itself:

JustDecompile Showing Exception Handling Structure.

JustDecompile Showing Exception Handling Structure.

Using the powers of imagination simply insert 20 or 30 lines of code (some to other helper methods all using types capable of throwing exceptions) in place of the MethodThatCanThrowAMultitudeOfExceptionTypes and you’re all set!

So, the core issue here is that setting an integer variable really isn’t giving us the feedback we need in every exception case (I don’t like this approach across the board but I digress!). Based on knowledge further up the call chain I knew that I must be hitting the general ‘Exception’ handler here which made my plight even worse; I could be hitting any old exception type (insert big sigh here).

Without the source files, I’m unable to directly manipulate the original code and improve my situation. Enter, ildasm.exe (launched here from a Visual Studio command prompt):

Example of Running ildasm.exe.

Example of Running ildasm.exe.

A full explanation of this tool can be found here:

Ildasm.exe

The tool, in a nutshell, can be used to inspect an assembly’s manifest file in a visual way and, most importantly, access the Common Intermediate Language (CIL, MSIL or just IL) that describe the assembly. I won’t go into an in-depth discussion of what IL is but, in short, it comprises of the platform-agnostic instructions formed after compilation along with type metadata. This code is compiled, on the fly, when required by a JIT (just-in-time) compiler that is geared towards the specific platform environment. In addition, the manifest file describes the actual assembly. Although lower-level in nature, IL is still fairly readable and can be dumped into an IL file from ildasm.exe by using the File > Dump command as shown below.

Dump Options for ildasm.exe.

Dump Options for ildasm.exe.

If you want to read up on the nature of what CIL is/how it is used, here is a wiki-based primer:

Common Intermediate Language

Anyway, here is the CIL for our troublesome member as it stands (along with a screen shot to show you roughly what you can expect to see after producing an IL file):

Generating and Inspecting CIL (IL).

Generating and Inspecting CIL (IL).

  .method public hidebysig instance int32 
          DoSomethingReallyCool() cil managed
  {
    // Code size       42 (0x2a)
    .maxstack  1
    .locals init (int32 V_0,
             int32 V_1)
    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  stloc.0
    .try
    {
      IL_0003:  nop
      IL_0004:  ldarg.0
      IL_0005:  call       instance void RandomApplication.MyApplication::MethodThatCanThrowAMultitudeOfExceptionTypes()
      IL_000a:  nop
      IL_000b:  nop
      IL_000c:  leave.s    IL_0023

    }  // end .try
    catch [mscorlib]System.InvalidOperationException 
    {
      IL_000e:  pop
      IL_000f:  nop
      IL_0010:  ldc.i4.1
      IL_0011:  stloc.0
      IL_0012:  nop
      IL_0013:  leave.s    IL_0023

    }  // end handler
    catch [mscorlib]System.IO.IOException 
    {
      IL_0015:  pop
      IL_0016:  nop
      IL_0017:  ldc.i4.2
      IL_0018:  stloc.0
      IL_0019:  nop
      IL_001a:  leave.s    IL_0023

    }  // end handler
    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  ldc.i4.3
      IL_001f:  stloc.0
      IL_0020:  nop
      IL_0021:  leave.s    IL_0023

    }  // end handler
    IL_0023:  nop
    IL_0024:  ldloc.0
    IL_0025:  stloc.1
    IL_0026:  br.s       IL_0028

    IL_0028:  ldloc.1
    IL_0029:  ret
  } // end of method MyApplication::DoSomethingReallyCool

The key issue, as previously stated, was the catch statement for the general ‘Exception’ type (IL defined as follows):

    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  ldc.i4.3
      IL_001f:  stloc.0
      IL_0020:  nop
      IL_0021:  leave.s    IL_0023

    }  // end handler

This certainly isn’t going to be a masterclass in IL coding but just as a demonstration I’m going to alter the catch handler as follows (using IL geared to throw the exception back up to the calling code). I’m sure this is an art in itself but I’m definitely not the one to teach you (maybe I’ll add it to the hit list as an interesting learning topic!). The statement begins with a ‘label’ (starting with ‘IL_’ and ending with a colon); one golden rule to remember here is not to duplicate any labels across statements. Doing so will result in errors when you try to recompile these instructions back into an exe or dll.

    catch [mscorlib]System.Exception 
    {
      IL_001c:  pop
      IL_001d:  nop
      IL_001e:  rethrow
    }  // end handler

With this change made I can now use another tool, called ilasm.exe, as follows to recompile these instructions back into an exe (although, this was a dll in the scenario I previously dealt with. Ilasm.exe allows for additional command flags to be specified, such as /dll, to determine the output file type):

Example of Running ilasm.exe.

Example of Running ilasm.exe.

Another word of warning at this point; I made the mistake here of recompiling my dll at the time (in the test scenario) using an incorrect version of ilasm.exe (targeting an incorrect .Net Framework). I lucked out by simply changing the IIS Application Pool to run under a newer .Net Framework version to save on recompiling the dll again. Certainly something to keep in mind however.

Full details on this specific tool can be found here:

Ilasm.exe

Looking at this recompiled assembly you can see that I now have the desired ‘throw’ statement, exactly where I need it:

JustDecompile Showing Updated Code.

JustDecompile Showing Updated Code.

In my test scenario I was able to push this dll into the given environment, use service trace logs to identify the exception (including stack trace and additional information) and finally fix all of the remaining issues.

So, if you’re stuck in future with .Net code you can’t directly alter and you have (caveat, you’ll have to work harder if the assembly is strongly-named/signed) an exe/dll that you need to make changes to in order to get meaningful outputs, consider this approach.

Until the next time, thanks all!

Code Project: Epic Quest! Unit Testing

A staple of TDD (Test Driven Development), although not quite how I’ve implemented it here (as I already have too much code hanging around prior to writing any tests), the humble Unit Test is something I’ve wanted to dedicate a little bit of time to for a while; so here it is.

Inclusion of Unit Tests in a project, from my own personal experience, is something that rarely takes as much precedence as it should. Sometimes they aren’t included at all; other times they are incredibly slim-line to the point of only scratching the surface – This feels like a bit of a shame. I won’t get into my own feelings too much on the matter beyond saying that I’m of the belief that they certainly have a place and add value.

To this end I’ve gone ahead and created a few simple Unit Tests covering some of the foundation classes of the EpicQuest code base as follows:

  • DungeonTests test class – Covering the EpicDungeon class.
  • RoomTests test class – Covering the Room class (and creation of room content).
  • CombatTests test class – Covering a very basic combat scenario which will be fleshed out further in due course.
  • WeaponTests test class – Covering a basic Weapon object creation scenario.
  • ArmourTests test class – Covering a basic Armour object creation scenario.
  • TreasureTests test class – Currently unimplemented (the concept of ‘treasure’ still needs consideration).

As a bit of a visual aid, here’s a snippet from Visual Studio showing the Unit Test project structure and a Class Diagram illustrating the setup:

Unit Test Project Configuration.

Unit Test Project Configuration.

Unit Test Class Diagram.

Unit Test Class Diagram.

One of key things to notice here is that the EpicQuest project has been referenced in order for us to get hold of the types we need to test. A key benefit I’ve gained since inclusion of the tests is that I’ve been forced to compartmentalise my code further to make testing distinct pieces of functionality possible. Some code has also been re-homed fully (i.e. whereby I had methods sitting within classes whereby it didn’t actually make sense for them to live there from a code structure point of view and, therefore, a Unit Testing perspective). A few difficult questions have also been posed in relation to the accessibility of certain members during creation of the test code; issues I wouldn’t have had to consider without travelling down this path. All in all, pretty positive outcomes so far.

So what’s the anatomy of a basic Unit Test, how is it triggered and how can we glean valuable information from the results?

Within Visual Studio at least, the first requirement is a Unit Test project, as the previous screen shot illustrates (EpicQuest.Tests). From here, a great place to begin is by adding a Basic Unit Test template class:

Add New Unit Test Dialog.

Add New Unit Test Dialog.

This will provide the following stub class adorned with the TestClass attribute with a single test method, suitably adorned with the TestMethod attribute. These attributes identify the object/members as test entities for exposure in the Test Explorer window which we’ll see later.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace EpicQuest.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {

        }
    }
}

Test methods, regardless of the functionality being tested, will normally follow a structure that looks like this:

/// <summary>
/// Test method that ensures that the EpicDungeon
/// instance level RemoveDungeonRoom method is working
/// as expected.
/// </summary>
[TestMethod]
public void EpicDungeonRoomRemovalTest()
{
	//Phase one - Arrange (essentially complete any configuration to actually perform the test - In this case 
	//create a dungeon and add a known room instance to it, taking note of the room count prior to removal of the room)
	Room roomToRemove = new Room();

	EpicDungeon dungeon = new EpicDungeon(EpicDungeon.GenerateRandomRoomConfiguration()); //Opted to create a random room configuration to strengthen the test scenario
	dungeon.AddDungeonRoom(roomToRemove);

	int previousRoomCount = dungeon.DungeonRooms.Count;

	//Phase two - Test/Act (remove the room)
	dungeon.RemoveDungeonRoom(roomToRemove);

	//Phase three - Test/Assert (run tests to ensure that the correct end result, after removal of the room, has been achieved)
	Assert.IsFalse(dungeon.DungeonRooms.Contains(roomToRemove), "EpicDungeonRoomRemovalTest (DungeonTests) failed. Test room roomToRemove still found in dungeon.DungeonRooms.");
	Assert.AreEqual<int>((previousRoomCount - 1), dungeon.DungeonRooms.Count, "EpicDungeonRoomRemovalTest (DungeonTests) failed. Room count (dungeon.DungeonRooms.Count) has an unexpected value.");
}

The structure factors in three distinct phases:

  • Arrange. This stage involves configuring any objects and setup as required by the test you plan to perform.
  • Act. Post configuration you actually need to call the functionality you wish to test.
  • Test. Lastly, assertions are run in order to see if the code under test worked as expected.

The MSDN documentation surrounding running Unit Tests through Visual Studio is well written and quite comprehensive. Further information can be found by accessing the following link:

Walkthrough: Creating and Running Unit Tests for Managed Code.

In order to see if the code under test is working as expected the static members of the Assert class come into play (of which there are a few, see the Assert Class MSDN documentation for a full listing).

In the above listing, I’ve opted to firstly use Assert.IsFalse to make a logical test to see if the Room instance I am expecting to be removed by the RemoveDungeonRoom method has actually be taken out of the EpicDungeon instance. Lastly, I’ve called on a generic version (which is awesome!) of Assert.AreEqual to make a secondary check to ensure the remaining count of Room objects in the EpicDungeon is as I would expect, following the previous Room object removal. The Assert static members allow for value types and objects to be passed in for inspection and, in the instances I’ve seen, for a string message to be specified; to be shown if the test fails. You’ll see other examples of Assert methods in action, such as the Assert.IsInstanceOfType, in later examples.

The Assert class and the members on offer are fairly intuitive right off the bat; you’ll probably only need a few minor peeks at the documentation to get underway if you’re familiar with C#.

NOTE: Some of the xml comments and variable names, etc have been tweaked throughout the code at varying times meaning some of the screen shots you see may fall out of line slightly with the code samples. If something is functionally different then I have gone back and altered the screen shots to align with the code.

As there’s not tonnes of code as of yet in the tests I’ve written here’s the full listing of what I have so far:

Dungeon Tests

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EpicQuest.Models.Dungeon;
using System.Collections.Generic;

namespace EpicQuest.Tests.Dungeon
{
    /// <summary>
    /// Dungeon entity specific Unit Tests
    /// for the Epic Quest game.
    /// </summary>
    [TestClass]
    public class DungeonTests
    {
        /// <summary>
        /// Test method that ensures that the EpicDungeon
        /// object (static) level GenerateRandomRoomConfiguration method 
        /// is working as expected. 
        /// </summary>
        [TestMethod]
        public void EpicDungeonRoomGenerationTest()
        {
            //Generate a random room amount seed and get an actual number of rooms as generated by EpicDungeon.GenerateRandomRoomConfiguration. Obtain the difference
            int roomNumberSeed = new Random().Next(3, 10), roomNumber = EpicDungeon.GenerateRandomRoomConfiguration(roomNumberSeed).Count,
                difference = (roomNumber - roomNumberSeed);

            //Extra debugging
            System.Diagnostics.Trace.WriteLine(string.Format("EpicDungeonRoomGenerationTest roomNumberSeed {0}; roomNumber: {1}; difference: {2}.", roomNumberSeed, roomNumber, difference));

            //The test is successful if the difference is between 0 and 2 (these are the rules that I've specified)
            if (difference < 0 || difference > 2)
            {
                Assert.Fail("EpicDungeonRoomGenerationTest (DungeonTests) failed. Room difference between seed number and actual, generated number out of range. Difference: {0}", difference);
            }
        }

        /// <summary>
        /// Test method that ensures that the EpicDungeon
        /// object constructor is working
        /// as expected.
        /// </summary>
        [TestMethod]
        public void EpicDungeonCreationTestOne()
        {
            //Get a predefined list of rooms and pass them into an EpicDungeon object constructor
            List<Room> testRooms = new List<Room>();
            testRooms.AddRange(new Room[3] { new Room(), new Room(), new Room() });

            EpicDungeon dungeon = new EpicDungeon(testRooms);

            //Test to ensure that dungeon.DungeonRooms is based on our testRooms collection
            Assert.AreEqual(testRooms, dungeon.DungeonRooms, "EpicDungeonCreationTestOne (DungeonTests) failed. Test room array is not represented by dungeon.DungeonRooms (different refs).");
        }

        /// <summary>
        /// Test method that ensures that the EpicDungeon
        /// object constructor is working
        /// as expected (very basic test).
        /// </summary>
        [TestMethod]
        public void EpicDungeonCreationTestTwoBasic()
        {
            //Test the constructor that takes an array of room objects
            Room[] roomArray = new Room[] { new Room(), new Room(), new Room() };

            EpicDungeon dungeon = new EpicDungeon(roomArray);

            //The room array is converted into a list within the Room class - Do a simple count check as a follow up to EpicDungeonCreationTestOne
            Assert.AreEqual<int>(roomArray.Length, dungeon.DungeonRooms.Count, "EpicDungeonCreationTestTwoBasic (DungeonTests) failed. Test array room count differs to dungeon.DungeonRooms.Count.");
        }

        /// <summary>
        /// Test method that ensures that the EpicDungeon
        /// instance level AddDungeonRoom method is working
        /// as expected.
        /// </summary>
        [TestMethod]
        public void EpicDungeonRoomAdditionTest()
        {
            EpicDungeon dungeon = new EpicDungeon(EpicDungeon.GenerateRandomRoomConfiguration());
            
            int previousRoomCount = dungeon.DungeonRooms.Count;

            Room roomToAdd = new Room();
            dungeon.AddDungeonRoom(roomToAdd);

            Assert.IsTrue(dungeon.DungeonRooms.Contains(roomToAdd), "EpicDungeonRoomAdditionTest (DungeonTests) failed. Test Room roomToAdd not found in dungeon.DungeonRooms.");
            Assert.AreEqual<int>((previousRoomCount + 1), dungeon.DungeonRooms.Count, "EpicDungeonRoomAdditionTest (DungeonTests) failed. dungeon.DungeonRooms.Count has an unexpected value.");
        }

        /// <summary>
        /// Test method that ensures that the EpicDungeon
        /// instance level RemoveDungeonRoom method is working
        /// as expected.
        /// </summary>
        [TestMethod]
        public void EpicDungeonRoomRemovalTest()
        {
            //Phase one - Arrange (essentially complete any configuration to actually perform the test - In this case 
            //create a dungeon and add a known room instance to it, taking note of the room count prior to removal of the room)
            Room roomToRemove = new Room();

            EpicDungeon dungeon = new EpicDungeon(EpicDungeon.GenerateRandomRoomConfiguration()); //Opted to create a random room configuration to strengthen the test scenario
            dungeon.AddDungeonRoom(roomToRemove);

            int previousRoomCount = dungeon.DungeonRooms.Count;

            //Phase two - Test/Act (remove the room)
            dungeon.RemoveDungeonRoom(roomToRemove);

            //Phase three - Test/Assert (run tests to ensure that the correct end result, after removal of the room, has been achieved)
            Assert.IsFalse(dungeon.DungeonRooms.Contains(roomToRemove), "EpicDungeonRoomRemovalTest (DungeonTests) failed. Test room roomToRemove still found in dungeon.DungeonRooms.");
            Assert.AreEqual<int>((previousRoomCount - 1), dungeon.DungeonRooms.Count, "EpicDungeonRoomRemovalTest (DungeonTests) failed. Room count (dungeon.DungeonRooms.Count) has an unexpected value.");
        }
    }
}

Room Tests

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EpicQuest.Models.Dungeon;
using EpicQuest.Interfaces;
using EpicQuest.Models;
using System.Collections.Generic;
using System.Linq;
using EpicQuest.Models.Items;

namespace EpicQuest.Tests.Rooms
{
    /// <summary>
    /// Room entity specific Unit Tests
    /// for the Epic Quest game.
    /// </summary>
    [TestClass]
    public class RoomTests
    {
        /// <summary>
        /// Test method that ensures that the Room
        /// object (static) level GenerateRandomRoomContent method 
        /// is working as expected. 
        /// </summary>
        [TestMethod]
        public void RoomContentGenerationTest()
        {
            //Test that Room.GenerateRandomRoomContent is producing content based on the seed provided
            int roomContentSeed = 2, roomContentNumber = Room.GenerateRandomRoomContent(roomContentSeed).Count, difference = (roomContentNumber - roomContentSeed);

            //Produce some extra debugging output
            System.Diagnostics.Trace.WriteLine(string.Format("RoomContentGenerationTest (RoomTests) roomContentSeed {0}; roomContentNumber: {1}; difference: {2}.", 
                roomContentSeed, roomContentNumber, difference));

            //Ensure the room content number produced is within the expected range (based on the seed)
            if (difference < 0 || difference > 2)
            {
                Assert.Fail("RoomContentGenerationTest (RoomTests) failed. Room content difference between seed number and actual, generated number out of range. Difference: {0}", difference);
            }
        }

        /// <summary>
        /// Test method that ensures that the Room
        /// object constructor is working as expected.
        /// </summary>
        [TestMethod]
        public void RoomContentCreationTestOne()
        {
            //Add some content to a list of type IRoomContent and pass this to the Room constructor
            List<IRoomContent> roomContentList = new List<IRoomContent>();
            roomContentList.AddRange(new IRoomContent[] { new Skeleton(), new Skeleton(), new Kobold(), new Kobold() });

            Room room = new Room(roomContentList);

            //Check to ensure that the content in the newly created Room object is based on the information passed to the constructor
            Assert.AreEqual(roomContentList, room.RoomFeatures, "RoomContentCreationTestOne (RoomTests) failed. Test room content array is not represented by room.RoomFeatures (different refs).");
        }

        /// <summary>
        /// Test method that ensures that the Room
        /// object constructor is working
        /// as expected (very basic test).
        /// </summary>
        [TestMethod]
        public void RoomContentCreationTestTwoBasic()
        {
            //Chuck an array of IRoomContent to the Room constructor
            IRoomContent[] roomContent = new IRoomContent[] { new Kobold(), new Skeleton() };

            Room room = new Room(roomContent);

            //Again, similar to the Dungeon test of a similiar nature, do a simple count of content items and ensure it's correct
            Assert.AreEqual<int>(roomContent.Length, room.RoomFeatures.Count, "RoomContentCreationTestTwoBasic (RoomTests) failed. Test array room Content count differs to room.RoomFeatures.Count.");
        }

        /// <summary>
        /// Test method that ensures that the Room
        /// instance level AddContentToRoom method is working
        /// as expected.
        /// </summary>
        [TestMethod]
        public void RoomContentAdditionTest()
        {
            //Create a new room and added random content (as base content). Take a note of the items of content currently in the room
            Room room = new Room(Room.GenerateRandomRoomContent());

            int currentRoomFeatureCount = room.RoomFeatures.Count;

            //Add a new monster to the room
            IRoomContent kobold = new Kobold(); 
            room.AddContentToRoom(kobold);

            //Ensure the monster has correctly been added to the room
            Assert.IsTrue(room.RoomFeatures.Contains(kobold), "RoomContentAdditionTest (RoomTests) failed. Test room feature kobold not found in room.RoomFeatures.");
            Assert.AreEqual<int>((currentRoomFeatureCount + 1), room.RoomFeatures.Count, "RoomContentAdditionTest (RoomTests) failed. Room feature count (room.RoomFeatures.Count) has an unexpected value.");
        }

        /// <summary>
        /// Test method that ensures that the Room
        /// instance level RemoveContentFromRoom method is working
        /// as expected. 
        /// </summary>
        [TestMethod]
        public void RoomContentRemovalTest()
        {
            //Add a new skeleton to a new room (with some other random content to strengthen the test example). Take a note of the count of room features prior to calling RemoveContentFromRoom
            IRoomContent skeleton = new Skeleton();

            Room room = new Room(Room.GenerateRandomRoomContent());
            room.AddContentToRoom(skeleton);

            int previousRoomFeatureCount = room.RoomFeatures.Count;

            //Run the test, remove the skeleton from the room
            room.RemoveContentFromRoom(skeleton);

            //Ensure that the known skeleton instance has been removed from the room (and that the count of remaining pieces of room content is correct)
            Assert.IsFalse(room.RoomFeatures.Contains(skeleton), "RoomContentRemovalTest (RoomTests) failed. Test room feature skeleton still found in room.RoomFeatures.");
            Assert.AreEqual<int>((previousRoomFeatureCount - 1), room.RoomFeatures.Count, "RoomContentRemovalTest (RoomTests) failed. Room feature count (room.RoomFeatures.Count) has an unexpected value.");
        }

        /// <summary>
        /// Test method that ensures that the Room
        /// instance level RoomClearedOfMonstersTest method 
        /// is working as expected. 
        /// </summary>
        [TestMethod]
        public void RoomClearedOfMonstersTest()
        {
            //Configure some fixed, known pieces of room content and add it to a new room
            IRoomContent skeleton = new Skeleton(), kobold = new Kobold(), treasureItem = new TreasureItem();

            Room room = new Room(skeleton, kobold, treasureItem);

            //Test that the room contains monsters at this point
            Assert.IsFalse(room.RoomClearedOfMonsters, "RoomClearedOfMonstersTest (RoomTests) failed. No monsters detected in the room.");

            //Remove just the fixed, known monsters - Leaving the treasure item intact
            room.RemoveContentFromRoom(skeleton, kobold);

            //Final round of tests. Ensure all monsters have been removed and that only one piece of content remains (the treasure item)
            Assert.IsTrue(room.RoomClearedOfMonsters, "RoomClearedOfMonstersTest (RoomTests) failed. Monsters detected in the room.");
            Assert.AreEqual<int>(room.RoomFeatures.Count, 1, "RoomClearedOfMonstersTest (RoomTests) failed. An unexpected room feature count was detected (room.RoomFeatures.Count).");
            Assert.IsInstanceOfType(room.RoomFeatures.First(), typeof(TreasureItem), "RoomClearedOfMonstersTest (RoomTests) failed. Remaining room feature is not of the expected type. Type detected: {0}",
                room.RoomFeatures.First().GetType().Name);
        }
    }
}
 

Combat Tests

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EpicQuest.Interfaces;
using EpicQuest.Models;
using EpicQuest.Utility;

namespace EpicQuest.Tests.Combat
{
    /// <summary>
    /// Combat based entity specific Unit Tests
    /// for the Epic Quest game. 
    /// </summary>
    [TestClass]
    public class CombatTests
    {
        /// <summary>
        /// Test method that ensures that the StaticGameActions
        /// object (static) level HandleCombat method 
        /// is working as expected.  
        /// </summary>
        [TestMethod]
        public void CombatTestOne()
        {
            //Setup the aggressor and defender ICombatant supporting objects
            ICombatant aggressor = new Brawler(), defender = new Skeleton();

            //Handle the combat. Take the defenders previous health and obtain the damage done from StaticGameActions.HandleCombat call
            int defenderPreviousHealth = defender.Health, damageDone = StaticGameActions.HandleCombat(aggressor, defender);

            //Test that the defenders health has been subtracted properly (TODO - Handle dead defenders and make sure this works if a defender is overkilled)
            Assert.AreEqual<int>((defenderPreviousHealth - damageDone), defender.Health);
        }
    }
}

Weapon Tests

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EpicQuest.Models;
using EpicQuest.Models.Dice;

namespace EpicQuest.Tests.Items
{
    /// <summary>
    /// Weapon entity specific Unit Tests
    /// for the Epic Quest game. 
    /// </summary>
    [TestClass]
    public class WeaponTests
    {
        /// <summary>
        /// Test method that ensures that the Weapon
        /// object constructor is working as expected.
        /// </summary>
        [TestMethod]
        public void WeaponTestBasic()
        {
            //Create a new Weapon
            Weapon weaponItem = new Weapon(GameEnums.HeroOffensiveItemType.LegendaryBastardSword);

            //Ensure that the die associated with the object are as expected
            Assert.IsTrue((weaponItem.Dice.Where(die => die is BlueDie).Count() == 1 && weaponItem.Dice.Where(die => die is RedDie).Count() == 1
                && weaponItem.Dice.Count == 2), "WeaponTestBasic (WeaponTests) failed. Incorrect type of die/die count detected.");
        }
    }
}

Armour Tests

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using EpicQuest.Models;
using EpicQuest.Models.Dice;

namespace EpicQuest.Tests.Items
{
    /// <summary>
    /// Armour entity specific Unit Tests
    /// for the Epic Quest game. 
    /// </summary>
    [TestClass]
    public class ArmourTests
    {
        /// <summary>
        /// Test method that ensures that the Armour
        /// object constructor is working as expected. 
        /// </summary>
        [TestMethod]
        public void ArmourTestBasic()
        {
            //Die property is publically accessible??? TO CHECK; Either way create a new, known armour type
            Armour armourPiece = new Armour() { DefensiveItemType = GameEnums.HeroDefensiveItemType.LeatherChestPiece, DefensiveItemTypeSlot = GameEnums.HeroDefensiveItemSlotType.Chest };

            //Ensure the die for this piece of armour are correct
            Assert.IsTrue((armourPiece.Dice.Where(die => die is GreenDie).Count() == 2 && armourPiece.Dice.Count == 2), "ArmourTestBasic (ArmourTests) failed. Incorrect type of die/die count detected.");
        }
    }
}

Treasure Item Tests

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace EpicQuest.Tests.Items
{
    /// <summary>
    /// TreasureItem entity specific Unit Tests
    /// for the Epic Quest game. 
    /// </summary>
    [TestClass]
    public class TreasureTests
    {
        /// <summary>
        /// TO DO.
        /// </summary>
        [TestMethod]
        public void TreasureTestBasic()
        {
            //TO DO, Treasure not fully implemented
        }     
    }
}

So how do you run these tests? It’s pretty damn easy – You just make use of the Test Explorer window in Visual Studio. A massive plus for me is that Unit Test projects, class templates and the Test Explorer are all available with the express edition of Visual Studio; big win! Either type ‘Test Explorer’ into the Quick Launch search bar in the top right hand corner of the Visual Studio UI or navigate to TEST > Windows > Test Explorer on the Visual Studio menu bar:

Accessing the Test Explorer Window.

Accessing the Test Explorer Window.

The Test Explorer window will automatically detect projects in scope that contain members marked with the TestMethod attribute (the classes of course need the TestClass attribute to). The Test Explorer window allows you to run all tests, previously failed tests, individual tests or a specific playlist of tests. The Test Explorer will appear like this in its initial state:

The Test Explorer Window (Initial State).

The Test Explorer Window (Initial State).

Here’s what the Test Explorer looks like after a successful test run. For starters, you get a run-down of how long each test took to run along with a link taking you to the location of the test method in the source code. The image shows an example of some additional debugging output:

Test Additional Output Example.

Test Additional Output Example.

In order to get hold of extra debugging output you can use the Trace.WriteLine static method as indicated in the following code snippet:

/// <summary>
/// Test method that ensures that the Room
/// object (static) level GenerateRandomRoomContent method 
/// is working as expected. 
/// </summary>
[TestMethod]
public void RoomContentGenerationTest()
{
	//Test that Room.GenerateRandomRoomContent is producing content based on the seed provided
	int roomContentSeed = 2, roomContentNumber = Room.GenerateRandomRoomContent(roomContentSeed).Count, difference = (roomContentNumber - roomContentSeed);

	//Produce some extra debugging output
	System.Diagnostics.Trace.WriteLine(string.Format("RoomContentGenerationTest (RoomTests) roomContentSeed {0}; roomContentNumber: {1}; difference: {2}.", 
		roomContentSeed, roomContentNumber, difference));

	//Ensure the room content number produced is within the expected range (based on the seed)
	if (difference < 0 || difference > 2)
	{
		Assert.Fail("RoomContentGenerationTest (RoomTests) failed. Room content difference between seed number and actual, generated number out of range. Difference: {0}", difference);
	}
}

Let’s rig a failure. I’ve simulated an underlying change to the code base that is going to trip up some of our Unit Tests. We receive the following output in the Test Explorer window after running all tests:

Test Explorer Failure Example.

Test Explorer Failure Example.

Two of our test methods fell over and a quick look at the output suggests that the Room class AddContentToRoom instance level method appears to be functioning unexpectedly (the room feature is not being added to the room). This is the state of the source code as it currently stands:

/// <summary>
/// Public method that allows a piece of content/multiple pieces
/// of content to be added to this particular Room.
/// </summary>
/// <param name="features">A single piece of content/array of content pieces to add to the Room.</param>
public void AddContentToRoom(params IRoomContent[] features)
{
	if (features != null && features.Count() > 0)
	{
		foreach (IRoomContent item in features)
		{
			roomFeatures.Add(new Kobold());
		}
	}
}

Looking through this code we can see that the method accepts an array of type IRoomContent named features. After a null check and a count to ensure the array is not empty we enter a simple foreach statement to add the IRoomContent supporting objects to the roomFeatures data field. The roomFeatures.Add statement is however simply adding a new Kobold object to the roomFeatures field list and ignoring the items in the array being passed in. Bad bug; slap on the wrists for introducing that!!! Let’s alter it to this:

/// <summary>
/// Public method that allows a piece of content/multiple pieces
/// of content to be added to this particular Room.
/// </summary>
/// <param name="features">A single piece of content/array of content pieces to add to the Room.</param>
public void AddContentToRoom(params IRoomContent[] features)
{
	if (features != null && features.Count() > 0)
	{
		foreach (IRoomContent item in features)
		{
			roomFeatures.Add(item);
		}
	}
}

Now we are using the foreach item variable correctly and adding this to the roomFeatures collection.

Back in the Test Explorer we are now able to explicitly re-run just the failed tests as shown:

Failed Tests in the Test Explorer Window.

Failed Tests in the Test Explorer Window.

The failed tests run up and we can see that these tests run without error, happy days!

Passed Tests in the Test Explorer Window.

Passed Tests in the Test Explorer Window.

As for Unit Testing, that’s about it for now. The only other small alteration I’ve made to the existing code base at this time is removal of the HandleCombat method from the GameManager class (to aid in Unit Testing/general code structure). This now resides in a new static class called StaticGameActions.

using System;
using EpicQuest.Interfaces;

namespace EpicQuest.Utility
{
    /// <summary>
    /// Public static utility class for the
    /// EpicDungeon game.
    /// </summary>
    public static class StaticGameActions
    {
        /// <summary>
        /// Private method that shows, in principle, how combat
        /// can be handled between two ICombatant based objects.
        /// </summary>
        /// <param name="aggressor">The instigator of the combat represented by a class implementing ICombatant.</param>
        /// <param name="target">The defender represented by a class implementing ICombatant.</param>
        public static int HandleCombat(ICombatant aggressor, ICombatant target)
        {
            //Prepare values representing the dice (random) and values to keep a tally of the offence, defence and difference totals
            int damageTotal = 0, defenceTotal = 0, difference = 0;
            Random rollDie = new Random();

            //Roll attack dice for the aggressor and get a damage total
            aggressor.AttackDice.ForEach(attackDie =>
            {
                damageTotal += (byte)attackDie.DieFaces[rollDie.Next(0, 5)];
            });

            //Roll defence dice for the defencder and get a defence total
            target.DefenceDice.ForEach(defenceDie =>
            {
                defenceTotal += (byte)defenceDie.DieFaces[rollDie.Next(0, 5)];
            });

            //Determine the difference between the damage/defence totals and deal damage to the target if required
            difference = damageTotal - defenceTotal;

            if (difference > 0)
            {
                target.Health -= difference;
            }

            return difference < 0 ? 0 : difference; //Only return non-negative results
        }
    }
}

That’s about it folks for the time being. Unit Testing is an interesting topic that requires some further investigation so I want to bring you another small piece looking at the TestInitialize and TestCleanup attributes and how more in depth tests can be constructed.

In other news, I’ve upgraded my blog account and got a new domain which I’m sure you’ll notice (splashed some cash as promised!). I’ll shortly be, as stated, making a few styling tweaks to the theme in addition to adding a dedicated Facebook page and Twitter feed, so watch this space.

Ciao for now.

SQL Debugging Tips – Part Two

A common task within SQL is to search an objects definition for a particular reference. This might be a table, column, procedure, function or view name. Below are a few techniques for achieving this. I’ve also highlighted some tables that are related to SQL Server Maintenance Plans and how to start searching interrogating the data tied to these.

/* 

Searching Object Definitions in SQL.

sys.sql_modules is a fairly complete way to achieve this but there's a myriad of options below, including a way 
to look into SQL Maintainence plans (add a where clause on the command column to filter this down)

*/

DECLARE
@SEARCH_TERM		NVARCHAR(MAX) = '%tblSYS_SCHEMA_COLUMN%';

--Full raw objects that reference tempID (or the SEARCH_TERM) in Views (for reference, select only), Triggers, SP's and Functions
SELECT
so.name
, sm.[definition]
, so.type_desc
FROM sys.sql_modules sm
	INNER JOIN sys.objects so ON sm.object_id = so.object_id
WHERE sm.[definition] LIKE @SEARCH_TERM
ORDER BY so.type_desc, so.name;

--Raw results from sys.procedures (SP's only) - Use for verification with the above sys.sql_modules results
SELECT *
FROM sys.procedures sp
WHERE OBJECT_DEFINITION(OBJECT_ID) LIKE @SEARCH_TERM
ORDER BY sp.name;

--Results from the INFORMATION_SCHEMA.ROUTINES system view (encapsulating SP's and Functions) - Use for verification with the above sys.sql_modules results
SELECT
ir.SPECIFIC_NAME
, ir.ROUTINE_TYPE
, ir.*
FROM INFORMATION_SCHEMA.ROUTINES ir
WHERE ir.ROUTINE_DEFINITION LIKE @SEARCH_TERM
ORDER BY ir.ROUTINE_TYPE DESC, ir.SPECIFIC_NAME;

--Check SQL Jobs (Maintenance Plans) for the search term being imbedded in the 
SELECT
sj.name
, sj.[enabled]
, sjs.step_name
, sjs.subsystem
, sjs.command		--Filter the command (by adding a WHERE clause) column by the search term (TSQL subsystems)
FROM msdb.dbo.sysjobs sj
	INNER JOIN msdb.dbo.sysjobsteps sjs ON sj.job_id = sjs.job_id
WHERE sjs.command LIKE @SEARCH_TERM
ORDER BY sj.name;

/*
SQL JOB TABLES FOR REFERENCE

SELECT * FROM msdb.dbo.SysJobs
SELECT * FROM msdb.dbo.SysJobSteps
SELECT * FROM msdb.dbo.SysJobSchedules
SELECT * FROM msdb.dbo.SysJobServers
SELECT * FROM msdb.dbo.SysJobHistory
*/