Bandwidth is no longer a problem

by Matt 21. January 2010 12:38

So that’s the blog back up and running then.

Turns out, this lowly little blog with 16 subscribers is using over 1Gb data transfer a month.

Gosh.

The hosting package I put this site on 3 years ago has a limit of 1Gb a month. Once I hit that limit, the site was suspended (or something broke on their end, I’m not too sure). I’ve now moved to GoDaddy for a while, which gives me a whopping 300Gb a month quota.

No, that’s not a typo. 300Gb. And that’s the economy plan, too.

But what surprises me is just how easy it is to use up 1Gb in a month. There’s not much on this site, and there’s really very little proper traffic. The vast majority of the bandwidth was being used up by spiders for search engines. I did some work to support 304 conditional gets, which bought me some time, but the site was being crawled every day and it soon adds up – 1Gb in a month comes down to about 30-odd Mb a day. Spread that amongst about 10 different spiders (of varying degrees of niceness) and (shock-horror) some actual real people browsing, and it soon goes.

That’s progress for you.

Tags:

Really advanced FireWatir – XUL!

by Matt 23. December 2009 15:49

This is a belated follow up to my previous couple of posts about taking FireWatir (or rather Jssh) further than simply accessing and manipulating the DOM. They were following the train of thought that since Firefox extensions have very deep access to the internals of the browser; they are written in JavaScript; and given that Jssh is just an extension, then the code you get Jssh to execute is going to be executed in the extensions context, and should allow you to do anything an extension can do.

That lead to getting a hold of Mozilla XPCOM interfaces and doing all sorts of interesting stuff – adding custom headers, changing Firefox preferences, manipulating stored cookies, or whatever else you can think of.

And now we get to take this to its logical conclusion.

Extensions are written in JavaScript and can have user interfaces, so the UI must be scriptable. And that would be XUL (think Ghostbusters), the XML User Interface Language, according to Wikipedia. It’s an XML dialect, very much analogous to HTML, but focussed on the user interface, not the page.

And Firefox exposes a DOM to manipulate it. You can iterate over the open windows using Jssh’s getWindows() function and examine the location. If it happens to start “chrome://” then the document property of that window is an XUL DOM.

Here’s a bit of script to automate the download save dialog, given the window from getWindows():

save_dialog.document.getElementById("save").click();
save_dialog.document.documentElement.getButton("accept").click();

This finds the “save” radio button element, clicks it, then clicks the accept button. Looks just like scripting the HTML DOM, doesn’t it?

Go to the C:\Program Files\Mozilla Firefox\chrome folder. Make a copy of toolkit.jar and rename it to toolkit.zip and extract the contents. Go to content\mozapps\downloads and there you’ll find the downloads.xul file with accompanying scripts and css. This is the content required to display the file download dialog. Open it in an editor to get an idea of what the object model will be like. And if you check for the location of the file download dialog from the getWindows() function, it’s at chrome://mozapps/content/downloads – so it’s fairly easy to find the appropriate xul file for the given location.

Here’s the Ruby code that will automate the download dialog:

def save_file_download
  jssh_command = "var save_dialog=null;"
  jssh_command += "for (i=0;i<getWindows().length;i++)"
  jssh_command += "{"
  jssh_command += "  var window = getWindows()[i];"
  jssh_command += "  if (window.location.toString().substring(0, 35) "
  jssh_command += "    == \"chrome://mozapps/content/downloads/\")"
  jssh_command += "  { save_dialog = window; break; }"
  jssh_command += "}"
  jssh_command += "if (save_dialog != null) {";
  jssh_command += "  save_dialog.document.getElementById(\"save\").click();"
  jssh_command += "  var acceptButton = save_dialog.document."
  jssh_command += "    documentElement.getButton(\"accept\");"
  jssh_command += "  acceptButton.disabled=false;"
  jssh_command += "  acceptButton.click();"
  jssh_command += "  true;"
  jssh_command += "} else { false; }"
  $jssh_socket.send("#{jssh_command}\n", 0)
  result = read_socket()
end

One very important point to make here: there is only one carriage return. Jssh prints out a prompt for each carriage return, and you need to do a read_socket() for each. Save it, and just do it once. This took me a while to figure out.

In fact, you could do a lot worse than taking a look at Ethan’s work on JsshObject which is some very nifty Ruby meta-programming that allows you to effectively write the JavaScript directly in the Ruby code. Very cool.

(The very observant among you might notice that I’m enabling the button before I click it. For some reason, the accept button is disabled unless the window has focus. I really can’t think of a good reason for this.

And if you’ve been paying particularly close attention to these posts (barring the two month gap!), I don’t actually use this code. I use the add_string_preference function from last post to add the expected mime type to the list of browser.helperApps.neverAsk.saveToDisk)

But this is a good example of the technique. It shows that browser dialogs can be automated – so hopefully no more frozen tests while displaying a JavaScript alert!

Tags: , , ,

Advanced FireWatir – cheat sheet

by Matt 20. October 2009 18:18

OK, so the last post was a bit heavy. Er, detailed. The upshot is that FireWatir, a Ruby library for automating Firefox via the JSSh (JavaScript Shell) extension can get access to the same APIs used by JavaScript Firefox extensions, allowing for some very low level automation of Firefox. The example in that post showed how to send arbitrary extra headers when navigating to a page:

# Essentially a copy of goto(uri) but can pass headers through the request
# Pass a list of headers, e.g. [ "X-Forwarded-For: 10.15.142.22", "cheese: toast" ]
# (Note that we should also be able to post data through this mozilla method)
def goto_with_headers(url, headers)
  #set_defaults()
  get_window_number()
  set_browser_document()
  h = ""
  headers.each {|value| h += "#{value}\\r\\n" }
  # Load the given url.
  jssh_command = "var headers ="
jssh_command += "Components.classes['@mozilla.org/io/string-input-stream;1']"
jssh_command += ".createInstance(Components.interfaces.nsIStringInputStream);
" jssh_command += " headers.setData(\"#{h}\", #{h.length});" # first null is referrer, second is postData jssh_command += " #{BROWSER_VAR}.webNavigation.loadURI(\"#{url}\", 0, null,”
jssh_command += "null, headers);
" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() wait() end

I thought it might be useful to show another example, without having to explain the background. If you’re interested in details, read the last post.

Right. Let’s automate Firefox’s preferences. Again, searching for what extensions do, they use the nsIPrefBranch interface to get and set preferences. We’ll do the same.

Instead of using the createInstance method to get the interface, we’re going to use getService (presumably because the preferences service object is already in memory, and we want to get a handle on that, rather than a new instance). In Javascript:

var prefs = Components.classes[\"@mozilla.org/preferences-service;1\"]
    .getService(Components.interfaces.nsIPrefBranch);"
prefs.setBoolPref("app.update.enabled", true);

And the Ruby helper method from my version of FireWatir::Firefox (note the escaped quotes):

def set_bool_preference(key, value)
  jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " prefs.setBoolPref(\"#{key}\", #{value});" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end

Other preferences are stored as strings and as comma separated string values:

def set_string_preference(key, value)
  jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " prefs.setCharPref(\"#{key}\", \"#{value}\");" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end def add_string_preference(key, value) jssh_command = "var prefs = Components"
jssh_command += ".classes[\"@mozilla.org/preferences-service;1\"]
" jssh_command += ".getService(Components.interfaces.nsIPrefBranch);" jssh_command += " var value = prefs.getCharPref(\"#{key}\");" jssh_command += " if (value === '') { value = \"#{value}\"; } else {" jssh_command += " var values = value.split(\",\");" jssh_command += " if (values.indexOf(\"#{value}\") === -1) {"
jssh_command += " values.push(\"#{value}\");"
jssh_command += " }
" jssh_command += " value = values.join(\",\");" jssh_command += " }" jssh_command += " prefs.setCharPref(\"#{key}\", value);" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() end

These can be used to make sure that your test environment’s version of Firefox is correctly setup before using. And if you’re wondering what to use for “key”, then take a browse through Firefox’s about:config (then Google it to see what it does). Here’s what I’m using at work:

browser.set_bool_preference("browser.download.manager.showWhenStarting", false)
# Convert "/" to "\". Double escaped because we're putting it into a JS string
browser.set_string_preference("browser.download.dir",
default_download_dir.gsub(/\//, '\\\\\\')) browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/zip") browser.add_string_preference("browser.helperApps.neverAsk.saveToDisk",
"application/postscript") browser.set_bool_preference("app.update.enabled", false) browser.set_bool_preference("browser.search.update", false) browser.set_bool_preference("extensions.update.enabled", false) browser.set_bool_preference("browser.sessionstore.resume_from_crash", false) browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://xxx.example.com") browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://yyy.examplecom") browser.add_string_preference("network.automatic-ntlm-auth.trusted-uris",
"http://zzz.example.com")

Most useful keys?

  • network.automatic-ntlm-auth.trusted-uris. With this, I can add a domain to automatically get NTLM authentication, so I know my tests won’t get stuck at an authentication prompt.
  • browser.helperApps.neverAsk.saveToDisk. I didn’t even know this one existed, but it’s great. This is a comma separated list of mime types that will always get saved to disk, so again, I know that I won’t get stuck at the confirm download prompt. (This is my favourite – previously, we’ve had to set these up by hand by actually downloading an instance of the file. I’m very happy to be automating this.)

Handy, eh?

One more post to go – let’s dive a bit deeper.

Tags: , , ,

Advanced FireWatir

by Matt 20. October 2009 17:41

There’s no delicate way to put this, so I’m just going to have to go ahead and say it; at work, we write automated acceptances tests in a BDD style, using cucumber and Ruby, and using FireWatir to automate Firefox.

Nothing controversial there, it’s just not the most thrilling of opening sentences.

This all started with the need to send a custom HTTP header to a web page we were testing. Now, we could write that using Ruby’s Net::HTTP module, but that would require also writing stuff to manage logins and cookies, and frankly, I’d rather let Firefox handle all that. It just needs to be convinced to send that extra header.

Now, FireWatir has a very interesting implementation. It uses an extension for Firefox called JSSh - a JavaScript Shell (which seems rather tricky to download – try the FireWatir download site). This extension starts listening on a socket, to which you can send raw JavaScript, which JSSh evaluates and returns the output. It exposes a fairly simple API that allows you to e.g. enumerate open windows, navigate to web pages, and access the DOM. FireWatir exercises this quite extensively.

Firefox really likes JavaScript. After all, most extensions are written in JavaScript.

And that right there is the key.

Firefox extensions are written in JavaScript, and they can do *anything* with the browser. So, I just have to figure out how an extension would add a header to a request. One quick Google later, and I’ve found nsIWebNavigation.

Firefox is written to be astonishingly componentised. It is implement on a platform called XPCOM (cross-platform COM, very much like Microsoft’s COM) that exposes a huge amount of functionality. All of which is publicly accessible to those JavaScript extensions.

And nsIWebNavigation is an XPCOM interface all about navigating the web browser, and one method it exposes is loadURI(url, flags, referrer, postData, headers). This method allows me to add custom headers to a navigation request – exactly what I’m looking for. I now have two small problems – how do I get my hands on an instance of nsIWebNavigation, and how do I marshal up the data into the headers parameter?

The first question was answered through a bit of trial and error. If you telnet into JSSh (telnet localhost 9997) you can start investigating your surroundings. You’ve got an interactive shell from which you can run commands, such as getWindow(). But if you type getWindow without the parentheses, you get the JS listing of the function definition. One such example lead me to discover that the higher-level API that JSSh exposes for navigation simply calls into the webNavigation property of the browser variable. And this property is an instance of nsIWebNavigation.

The second problem was solved by Google. I need an instance of nsIInputStream to represent the headers. Turns out I can create an instance of nsIStringInputStream, set the data from a JS string and pass that to loadURI. All of which gives us the following JavaScript:

var headers = Components.classes['@mozilla.org/io/string-input-stream;1']
    .createInstance(Components.interfaces.nsIStringInputStream);
headers.setData("X-Forwarded-For: 10.15.142.22\r\n", 31);
#{BROWSER_VAR}.webNavigation.loadURI("http://...", 0, null, null, headers);

The first line looks like some heavy magic. I won’t confess to knowing exactly what’s going on here, but it’s safe to assume that it’s creating an instance of a named XPCOM class and returning the nsIStringInputStream interface implemented by that instance. The second line sets the data into the object, pushing in a string (the loadURI docs state that each header must be separated by a carriage return/line feed pair) and the length of the string. The headers variable is then passed to loadURI, and ta-da! the browser is now navigated to the given URL, and the custom header is sent.

Of course, that’s just the JavaScript. We need to be able to use this from Ruby. I opened up the Firewatir::Firefox class and added:

# Essentially a copy of goto(uri) but can pass headers through the request
# Pass a list of headers, e.g. [ "X-Forwarded-For: 10.15.142.22", "cheese: toast" ]
# (Note that we should also be able to post data through this mozilla method)
def goto_with_headers(url, headers)
  #set_defaults()
  get_window_number()
  set_browser_document()
  h = ""
  headers.each {|value| h += "#{value}\\r\\n" }
  # Load the given url.
  jssh_command = "var headers = Components"
jssh_command += ".classes['@mozilla.org/io/string-input-stream;1']"
jssh_command += ".createInstance(Components.interfaces.nsIStringInputStream);
" jssh_command += " headers.setData(\"#{h}\", #{h.length});" # first null is referrer, second is postData jssh_command += " #{BROWSER_VAR}.webNavigation.loadURI(\"#{url}\", 0, null,"
jssh_command += " null, headers);
" $jssh_socket.send("#{jssh_command}\n", 0) read_socket() wait() end

Note that the carriage return/line feed escape characters and the quotes in setData have been escaped – we’re writing Ruby that is going to be writing JavaScript. And as the comment says – call it with an array of headers that you’d like to pass to the server.

So there we have it. FireWatir, via JSSh, has a much larger API available to it, thanks to XPCOM. This opens the door to some very interesting possibilities.

I’m aware this is quite an exposition heavy post, so I’ll do an executive summary with a few more examples (would automating Firefox preferences be useful?) and then we’ll get really advanced.

Tags: , , ,

iTunes 9 spoils my fun

by Matt 11. September 2009 18:17

Last week, I had a great idea on the train. This week, Apple released iTunes 9.

These two events are related. Bear with me.

I don’t really like iTunes. It’s slow (terribly, painfully, unresponsively slow), ugly and the music glitches when you play it. But I have an iPod and listen to various podcasts, so necessity wins.

And I use it on the train. Usually minimised into the little taskbar mini-player, which is fine. But then I got Windows 7 envy. I wanted thumbnail toolbars and jump lists, just like Windows Media Player.

Cue my great idea. I remembered that iTunes has two different types of SDKs – the more well known COM based SDK, which allows out-of-process control of the application, and a much more interesting Visual Plug-in system. This is a very low level, C-based API for loading dll’s directly into iTunes. It’s mainly intended for creating graphical visualisations of the currently playing tracks, but it can also (rather intriguingly) be used for “device” plugins, which you need to sign an NDA for.

And of course, once you’ve got a dll loaded into the process you can do anything you want, such as, oh, I don’t know, support thumbnail toolbars and jump lists. Native code and the latest whizzy Windows 7 features. What a fun little project.

Until this week, when Apple released iTunes 9.

It’s prettier, looking much more at home in the washed out, over-exposed Windows 7 palette, and astonishingly, it’s faster. Much faster. Significantly faster. I can navigate between my music library and my iPod without having to go for a cup of coffee. Hooray. (The music still glitches, which is unforgivable, but it’s much better. It’s almost winning me over.)

Oh yeah, and they’ve added thumbnail toolbars and jump lists.

iTunesWin7

Thanks Apple. Thanks a bunch. Spoilsports.

Tags: , , ,

Assembly binding redirects and pathological edge cases

by Matt 27. August 2009 19:07

I posted a little while ago that ReSharper had updated to 4.5.1, and that the test runner in xunitcontrib worked just fine with it, even though it was actually compiled against 4.5.

Except for one little edge case – all of the xunitcontrib tests themselves.

That was a little… perturbing.

ReSharperRunnerLoggerTests.ExceptionThrownCallsReportsExceptionAndFinishesClassTask : Failed

System.IO.FileLoadException: Could not load file or assembly
'JetBrains.ReSharper.TaskRunnerFramework, Version=4.5.1231.7, 
Culture=neutral, PublicKeyToken=1010a0d8d6380325' or one of its dependencies.
The located assembly's manifest definition does not match the assembly reference.
(Exception from HRESULT: 0x80131040) 

So what’s happening here?

The test runner plugin, compiled against ReSharper 4.5, is running (successfully) in the ReSharper 4.5.1 test runner. It is trying to execute its own test assembly, which is also compiled against ReSharper 4.5 (aka 4.5.1231.7). This test assembly is unable to find the ReSharper 4.5 assemblies, which are deployed into the same directory as the test assembly (I checked. Several times). And just to reiterate – the test runner plugin had no such assembly loading issues.

Surprisingly, this does make sense. It just took me a while to figure out.

Let’s start with the easy stuff. The test runner plugin is working due to plain old assembly redirects. ReSharper runs tests out of process, in an executable called JetBrains.ReSharper.TaskRunner.exe. If you look in its config file, you’ll see a ton of assembly redirects to map all ReSharper assemblies from 4.5.0.0 and above to 4.5.1274.1 (ReSharper 4.5.1). So, when xunitcontrib is used to run some tests, the exe fires up, loads the plugin, redirects my dependencies to the newer versions and all is well.

(xunitcontrib also runs in the devenv.exe process. A quick look at the devenv.exe.config file sees another ton of redirects. I know it’s the correct way to do this, but it does feel a bit crufty stomping all of that into someone else’s config…)

That explains why the test runner works. Why don’t the tests?

Thanks to the very same redirects that make the test runner work.

Tests are run in the same JetBrains.ReSharper.TaskRunner.exe as the plugin, so the same redirects apply. The problem starts because the ReSharper test runner sets up some custom assembly resolving handling, meaning that when the CLR tries to find a ReSharper assembly, the test runner can point it to the ReSharper installation directory. But because the tests run in a new AppDomain, they don’t get this custom assembly lookup, and so can’t find the new assemblies. Annoyingly, it reports this as being unable to load the originally requested version of the assembly (the version which is in the same directory, causing lots of confusion).

There are two solutions to this.

Obvious: recompile to 4.5.1.

Simple: add an empty app.config file. When the new AppDomain is created, it uses this config file, so no more redirects and no more failing tests.

So, if you ever get stuck trying to dogfood a test runner to run its own tests in another AppDomain within the same process while redirecting assembly references - this post’s for you.

Tags: , , , ,

Separate naming styles for tests in ReSharper

by Matt 1. August 2009 18:38

The aim of my last post was to let folks know that the existing release of xunitcontrib works just fine with the recently released ReSharper 4.5.1, oh-and-by-the-way a nice new feature is supported out of the box.

But it occurs to me that the nice new feature is interesting in and of itself, and I haven’t seen it mentioned much elsewhere. It allows you to specify different naming conventions for test and normal class and method names, easily adding support for BDD style naming. And it works with nunit, mstest and (with xunitcontrib installed) xunit.

So, in case you skipped the last post because you’re not interested in xunit or xunitcontrib, allow me to do a codinghorror and shamelessly link to myself for instructions and pictures. It talks a lot about xunit, but it applies to all test providers.

And if have already read it, please forgive the repeat and don’t bother reading this one.

Tags:

xunitcontrib + ReSharper 4.5.1

by Matt 31. July 2009 16:03

Just the other day, JetBrains released ReSharper 4.5.1. There are no major changes; it’s a maintenance release.

And the good news is, the latest version of xunitcontrib (0.3.1) works just fine with it. No changes. Nothing.

But there’s one nice, new, quiet, little feature.

ReSharper 4.5 added naming style rules, such that you would get a visible squiggly warning when classes weren’t named InCamelCase and variables weren’t lowerCaseCamel.

Too bad for those upstart BDD folks that like to ape Ruby with Significant_use_of_the_underscore_character when naming their tests (“underscore porn”). These guys would get a warning under their test methods.

ReSharper warns that test method does not match naming standard

We can fix this in 4.5.1 by adding a user defined naming rule. Go to the ReSharper options dialog, and select Naming Style from the tree on the left hand side. Then select Add in the bottom half of this dialog. We get a new window:

The ReSharper user defined naming rule dialog

We want to set the rule just as in the picture – give it a description, select Test type and Test method (property) and then select First_upper.

And now our Interestingly_named_methods are no longer marked as being non-standard, and our “normally” named tests are prompted for renaming.

BoringCSharpNamingStandard

Cool. But what about your existing, non-BDD style tests? You don’t have to rename those. Simply add a new naming style to the rule you’ve just created. So not only does the rule for test elements enforce Uppercase_with_underscores, it also enforces UpperCamelCase. Very handy.

And in case you missed why I’m pointing this out, ReSharper is smart enough to realise that this rule applies to any test identified by a test provider, such as xunitcontrib. Those examples above are xunit Facts.

And the best bit? I didn’t have to do any work.

Tags: , ,

xunitcontrib 0.3.1 bug fix release

by Matt 15. July 2009 19:51

I’ve just released a minor bug fix release to the xunitcontrib ReSharper runner.

As part of the recent rewrite to support version independence, I inadvertently broke the current directory. If you know what I mean. Previously, ReSharper was setting the current directory to be the location of the test assembly. Because I’m now handing off to the xunit runner API, ReSharper’s code was no longer being used, and the current directory remained pointing to the location of the plugin assembly.

Since this was quite a major change in behaviour, and very much a blocking issue, I wanted to get a fix out as quickly as possible. Hence release 0.3.1. Nothing else has changed, so if you’re affected by this issue, please go get the latest version.

And thanks for the feedback!

Tags: ,

xunitcontrib 0.3 – external annotations and live templates

by Matt 9. July 2009 20:33

As part of the latest release of xunitcontrib, I’ve added two new features, support for ReSharper’s External Annotations and Live Templates.

External Annotations

ReSharper performs some static analysis on your code, and displays a blue squiggly error indicator over anything it thinks will be a problem. One great example is possible System.NullReferenceExceptions – using an object which may be null.

Possible System.NullReferenceException is flagged 

One thing you might notice from that screenshot is that we’ve got an Assert.NotNull in there. This method throws an exception if the “otherFlagsAttribute” is null, meaning we can’t get a NullReferenceException. But ReSharper doesn’t know that, and so we still get the warning.

As I understand it, ReSharper can only analyse your source code, and not any compiled assemblies. So how do we tell it that Assert.NotNull ensures that we don’t get a NullReferenceException?

Well, ReSharper makes available an assembly called JetBrains.Annotations.dll that provides a number of attributes that can handle this scenario (plus a whole bunch more, like not null parameters, or marking parameters as string format specifiers). Handy, but a bit intrusive to your code, and not much use if you’ve not got the code available to change.

And that’s where External Annotations come in. You can now unobtrusively apply these attributes to the code through xml files. (And the ReSharper guys have very nicely produced files for the entire .net framework…)

So, that’s what this feature is. It adds the xml file telling ReSharper that xunit’s Assert.NotNull is an assert method that will stop execution of the method if the parameter is null. Similarly, there’s annotations for Assert.Null, Assert.True and Assert.False.

(Interestingly, it’s easy to concoct an example for NotNull and also for True or False (simply create an expression that is always false or always true) but I can’t create an example for Assert.Null. Answers on a postcard, please…)

To install, simply drop the xunit.xml file into ProgramFiles\JetBrains\ReSharper\v4.x\bin\External Annotations.

Live Templates

Live Templates are very much like Visual Studio’s built in snippets feature (I guess I ought to look into porting this to snippets…) but with a bit more oomph. They allow you to type an identifier that automatically expands into something useful. For example, the Live Template provided expands the word “fact” into:

"fact" live template expanded as a fact method

Which has the method name highlighted ready to edit, and when you hit enter, places the cursor into the body of the method. It also automatically adds any using statements necessary.

These are very useful, especially with the smart Intellisense shown for the assert templates:

Assert.Equal Live Template with smart intellisense

I’ve actually released two versions of this. One which uses acronyms such as “ae” and “ann” to represent “Assert.Equal” and “Assert.NotNull”, and another that uses similar acronyms, but beginning with “x” (“xe”, “xnn”). This is to avoid a collision with Live Templates for a certain other unit testing framework that I use.

Here’s a list of the Live Templates included (the words wrapped in dollar signs like $this$ are macros, and are quickly edited by pressing tab, and usually display some sort of useful Intellisense):

Shortcut #1 Shortcut #2 Expands to
ae xe Assert.Equal($expected$, $actual$)
at xt Assert.True($value$)
af xf Assert.False($value$)
ac xc Assert.Contains($expected$, $actual$)
an xn Assert.Null($value$)
ann xnn Assert.NotNull($value$)
athr xthr Assert.Throws<$T$>()
aiaf xiaf Assert.IsAssignableFrom<$T$>($object$)
ait xit Assert.IsType<$T$>($object$)
fact fact Creates an attributed test/fact method
fa fa [Fact]
theory theory Creates an attributes theory method
ta ta [Theory]
ida ida [InlineData()]

Note that not all of the assert methods are there. Only the most common have been added. And I haven’t added Assert.Same, namely because “as” is a poor acronym to go for.

This is a little trickier to install. In Visual Studio, go to ReSharper –> Live Templates, then click the button to “Mount storage” and navigate to and select the live template file of your choice (“ae” or “xe” format).

If you mount the file, then any changes are saved back to this file. If you import it, then any changes are kept in a copy of the config file. I prefer mounting the file, as it gives me more flexibility to change the templates and pass the changes around.

So that covers external annotations and live templates. As ever, if you have any bugs or comments, please use the Issues and Discussion pages of the CodePlex project site. And feel free to get in touch on Twitter, too (via @citizenmatt or #xunitcontrib).

Tags: ,

Rel=Me

Month List

RecentComments

Comment RSS