Testing a SignalR application using WebDriver in IE9

16/04/2013 § Leave a comment

I was having problems testing a SignalR application in IE9 using the Selenium IEServerDriver – the WebDriver instance would block indefinitely after navigating to a page that starts a hub connection.

SignalR Issue 293 seems to imply that the IE WebDriver is not compatible with foreverFrame – it never recognises that the page has finished loading. Changing the hub connection code to:


$.connection.hub.start({ transport: ['longPolling', 'webSockets'] });

fixed the issue and made IE9 testable again via WebDriver.

FakeRavenQueryable<T>

28/11/2012 § 3 Comments

We were recently trying to build basic unit tests for the controller actions on an MVC4 + RavenDB application, and having problems attempting to mock the IDocumentSession. The RavenDB people consistently say not to do that, and that running an EmbeddedDocumentStore solves every unit test problem under the sun and then makes you breakfast. However, we tried it and weren’t really happy with the code-to-value ratio.

The process is typically:

  1. Create an EmbeddedDocumentStore
  2. Create all your indexes
  3. Create a session, load your test document set, and save changes
  4. Wait for indexing to complete (eg by registering a custom IQueryListener that modifies all queries to wait for non-stale results)
  5. Create & inject your session
  6. Run your tests

This approach requires a lot of setup, the tests are slow, and the package dependencies on your test project are considerable, where all we really wanted to accomplish was to return a specific result set in response to a specific method call on the IDocumentSession.

The most immediate problem you run into when mocking IDocumentSession is returning a useable IRavenQueryable<T>. In case anyone else is brave enough to risk the scorn of Ayende, below is my implementation of a FakeRavenQueryable<T> class that wraps a generic IQueryable<T>:

public class FakeRavenQueryable<T> : IRavenQueryable<T>
    {
        private IQueryable<T> source;

        public RavenQueryStatistics QueryStatistics { get; set; }

        public FakeRavenQueryable(IQueryable<T> source, RavenQueryStatistics stats = null)
        {
            this.source = source;
            QueryStatistics = stats;
        }

        public IRavenQueryable<T> Customize(Action<Raven.Client.IDocumentQueryCustomization> action)
        {
            return this;
        }

        public IRavenQueryable<T> Statistics(out RavenQueryStatistics stats)
        {
            stats = QueryStatistics;
            return this;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return source.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return source.GetEnumerator();
        }

        public Type ElementType
        {
            get { return typeof(T); }
        }

        public System.Linq.Expressions.Expression Expression
        {
            get { return source.Expression; }
        }

        public IQueryProvider Provider
        {
            get { return new FakeRavenQueryProvider(source, QueryStatistics); }
        }
    }

    public class FakeRavenQueryProvider : IQueryProvider
    {
        private IQueryable source;
        private RavenQueryStatistics stats;

        public FakeRavenQueryProvider(IQueryable source, RavenQueryStatistics stats = null)
        {
            this.source = source;
            this.stats = stats;
        }

        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
            return new FakeRavenQueryable<TElement>(source.Provider.CreateQuery<TElement>(expression), stats);
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {

            var type = typeof(FakeRavenQueryable<>).MakeGenericType(expression.Type);
            return (IQueryable)Activator.CreateInstance(type, source.Provider.CreateQuery(expression), stats);
        }

        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
            return source.Provider.Execute<TResult>(expression);
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
            return source.Provider.Execute(expression);
        }
    }

It can be returned from a mocked IDocumentSession using code like the following (using Moq in this case):

    Mock<IDocumentSession> session = new Mock<IDocumentSession>();
    session.Setup(s => s.Query<Product>()).Returns(
        new FakeRavenQueryable<Product>(
            productList.AsQueryable(), 
            new RavenQueryStatistics { TotalResults = 100 }
        )
    );
    
    // inject your session & run your tests here
    RavenQueryStatistics stats;
    var results = session.Object.Query<Product>().Where(p => p.Id == "Products/1")
        .Statistics(out stats)
        .Take(1)
        .ToList();

It won’t make you breakfast, give you full access to the advanced session methods, or tell you if you’re passing unsupported expressions, but it will allow you to mock out simple queries in your application, and the Linq methods all work over your test list as you’d expect (even In!)

Using NuGet packages in Visual Studio 2012 project templates

17/09/2012 § 7 Comments

Anyone creating Visual Studio project templates these days should be using NuGet package references for project dependencies — it saves you having to update your templates every time a dependency changes, and follows a standard & generally accepted binary dependancy approach.

If you’re distributing your project template as a VSIX package (strongly recommended) the NuGet docs here specify the preferred approach is to include the .nupkg files within the VSIX, but the instructions specify the addition of a ‘CustomExtension’ element to the .vsixmanifest file that is no longer valid in v2 of the VSIX schema (the default in Visual Studio 2012). I spent a considerable period of time attempting to work out what the v2 equivalent of CustomExtension was, but to cut a long story short, you don’t need to make any changes to the .vsixmanifest — it’s enough to include all of the packages in the VSIX under a ‘Packages’ directory.

Following are the steps I used to create a working 2012 VSIX project template:

  1. Download and install the Visual Studio 2012 SDK, if you haven’t done so already.
  2. Create a solution and add a ‘C# Project Template’ project and a ‘VSIX Project’ project (under ‘Visual C# > Extensibility’). It amuses me no end that there’s a project template project template. I am easily amused though.
  3. Set up your project template the way you want it. I find it easier to create a temporary project, use ‘File > Export Template…’ and then unzip the package and copy the relevant bits across.
  4. Update your .vstemplate with the ‘WizardExtension’ and ‘WizardData’ elements like the following:
      <WizardExtension>
        <Assembly>NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
        <FullClassName>NuGet.VisualStudio.TemplateWizard</FullClassName>
      </WizardExtension>
      <WizardData>
        <packages repository="extension" repositoryId="<!-- your VSIX Product Id -->">
          <!-- your NuGet package dependencies go here, eg: -->
          <package id="Moq" version="4.0.10827" />
          <package id="xunit" version="1.9.1" />
          <package id="xunit.extensions" version="1.9.1" />
        </packages>
      </WizardData>
    
  5. Double-click on the source.extension.vsixmanifest to open it in the designer, and add a new Asset. Select ‘Microsoft.VisualStudio.ProjectTemplate’ as the type, “a project in the current solution” as the source, and then choose your template project. See this blog post for more info about this approach.
  6. Create a folder under your VSIX project called ‘Packages’ and drag the .nupkg files (referenced in the .vstemplate above) into this folder. Set the Build Type on the files to ‘Content’ and ‘Include in VSIX’ to ‘True’.
  7. Build the solution – it will produce a VSIX that includes the NuGet dependencies. You’re done!

It’s more correct to also add the NuGet Package Manager extension as a dependency to your VSIX, although I don’t usually bother for our internal templates.

One of the great things about VSIX distribution is that you can add multiple project & item templates to a single package as a discrete, easily distributable approach (just add more assets to the vsixmanifest).

Apps can’t link to the internet

15/09/2012 § Leave a comment

Some App Store reviewers appear to be taking an extremely hard line on review guideline 11.13:

Apps that link to external mechanisms for purchases or subscriptions to be used in the App, such as a “buy” button that goes to a web site to purchase a digital book, will be rejected

One of our apps was pinged for this despite having no purchase mechanism outside IAP, no purchase options on our website, no links to third parties like DropBox, and nothing else other than a couple of PDF download links in app. When asked for clarification, the Review Team responded with:

“remove any links that link out of the app and external links in the application description.”

Apparently linking to ‘The Internet’ might indirectly lead the user to purchase something outside of the App Store. I’d like to think that this is a single reviewer that’s received dud training; if not I expect Apple will need to update 11.13 to explicitly ban external links.

Update: An attempt to clarify whether all external links are in fact banned, or whether the reviewer felt there was an external purchase mechanism buried somewhere on our site, was rebuffed with:

“Thank you for your feedback. If you wish to appeal your review, you can submit a request to the App Review Board.”

Hardware Review: Leopold FC700R Tenkeyless

27/07/2012 § 4 Comments

My venerable (ca. 2002) Apple Pro keyboard recently died after an unfortunate altercation with a glass of water, so I’ve spent a bit of time looking for a good replacement. I’ve been interested in trying a mechanical keyboard for some time, especially recently after reading the opinions of people like John Gruber and Shawn Blanc, but because I work in an open-plan office in close proximity to four other guys, I was wary of getting anything too loud.

Most mac-based mechanical keyboard aficionados point to the following:

All of these offer a standard Mac keyboard layout, with different mechanical switch options.  Based on various reviews, I was leaning towards the Das, but no-one in Australia stocks the Mac version, and it doesn’t come in the ‘Silent’ (Cherry MX Brown) configuration. However, while I was trawling through the Australian computer hardware sites (such as PC Case Gear and Aus PC Market)  I noticed there were a significant number of mechanical keyboards catering to the gamer community. They didn’t offer a Mac layout, but the majority were based around the Cherry MX keyswitches (like the Das), and most had the option of an 87 key ‘tenkeyless’ form factor that excluded the number pad (like the Apple bluetooth/Macbook keyboards), but retained full-sized arrow & function keys like my old Apple Pro. I never use the number pad, so I was intrigued with this option — more desk space for Magic Trackpads & the like can only be a good thing.

I ended up going for a white Leopold FC700R tenkeyless keyboard with the Cherry MX Brown (tactile/no-click) keyswitches, from PC Case Gear ($120 + shipping). Leopold is a Korean company making Cherry MX-based keyboards that appear very similar to the popular Filco Majestouch line, and they’re also the only Cherry-switched keyboards sold by the US mechanical keyboard specialist Elite Keyboards.
Leopold FC700R


The keyboard arrived promptly (thanks PC Case Gear), and after reading the enlightening Engrish on the outside of the box, I managed to get it set up on the Macbook without much trouble. Modifier Key Options OS XI had to swap the Alt & Windows (Option & Command) keys over to preserve my sanity — there’s a built in facility in OS X System Prefs to alter modifier key functions, and the keycaps were easily pried off & swapped over. The side-printed ‘Media Player’ function keys all worked perfectly out of the box with the Fn modifier (I have the OS X ‘Standard Function Keys’ setting enabled), but the more obscure functions — screen brightness, mission control, keyboard backlight etc. aren’t available. I also managed to accidentally turn on the F5 LED (I presume this is the Num Lock?) a couple of times, and couldn’t turn it off again without unplugging the keyboard — unsure what’s causing that.

Physically, the keyboard is a solid, heavy, confidence-inspiring chunk of kit. Despite being smaller, it’s significantly heavier than the old Apple Pro keyboard. The weight, combined with generous, albeit low-fi, rubber footpads, mean the keyboard remains firmly anchored wherever it’s placed on the desk. The tenkeyless form factor is ideal for a modern desktop: the full sized function and arrow keys (plus dedicated forward-delete) are much appreciated, particularly given the amount of time I spend in Windows via Fusion, but you don’t feel it dominates the desk as much as a traditional 104 key full-sized keyboard. The body is simple and unadorned, and doesn’t consume any more desk footprint than it absolutely needs. Many people list the removable USB cable and three-way routing channel as a plus — I don’t see it as a significant benefit, but some do, so I’ll mention it all the same.

The keyboard came with a high-quality silicone keyboard protector that looks like something out of the early 90’s. It actually works pretty well in terms of providing minimal typing impedance and maximal glass-of-water impedance, but I felt it detracted from the tactile feel a little too much, so I’m not using it.

Leopold Keycap closeupThe most significant drawback (from my perspective, as someone accustomed to Apple polish) is the quality of the keycap printing. Most online retailers claim the Leopold keycaps are laser engraved. I suspect this only applies to the black keycaps — while I’m no expert, the white keycaps look more like they’ve been pad printed (they’re a bit uneven and have a clear coating). In addition, the text/artwork is not great; I’ve found having both “CapsLock” and “Back space” on the same keyboard to be a bit incongruous. Obviously buying the ‘Otaku’ blank keycaps would avoid that issue if you’re so inclined, but the availability of the white Otaku version seems limited.

If the biggest drawback is purely cosmetic, in contrast the biggest plus is purely functional. The keyboard feel is superb. I’m not sure if I can adequately explain the difference between a tactile mechanical keyboard and the more common dome or scissor models; you can either try it for yourself or take it on faith that they’re worth it. Noise-wise, the Cherry MX Brown switches are great for the office — my colleague is capable (and frequently so inclined) of pounding his Apple bluetooth keyboard much louder than I usually get out of the Leopold. The keys make some degree of noise bottoming out on the plate — more of a soft plasticy ‘clack’ than the metallic clickiness I recall from old-school keyboards — but it’s not enough to be disruptive. Soft landing pads would probably further reduce this if needed.

In summary

Pros:

  • Cherry MX Brown switches are veeerry nice
  • Compact tenkeyless form factor is highly recommended
  • Solid, quality construction

Cons:

  • Poor quality keycap printing
  • Odd F5 LED behaviour

If you use a keyboard all day, every day, it’s worthwhile finding one you enjoy using. I’m enjoying the Leopold.

 

Office365 password reset doesn’t work on Telstra phones

17/07/2012 § Leave a comment

We’ve had persistent problems getting password reset SMSes via the Office365 administrator password reset functionality. Some to-ing & fro-ing with Microsoft support has eventually uncovered that their SMSes are being rejected by Telstra due to an ‘invalid format’.

They claim to be looking into it, but in the meantime if you’re an Office365 administrator and have a Telstra mobile, make sure you have a backup administrator account.

Replacing Twitter – easier said than done

30/06/2012 § Leave a comment

The normally unflappable Brent Simmons suggests we replace twitter with ‘nothing’. This is an interesting yet logistically improbable approach — I’ve tried to enumerate the challenges here, along with the reasons why I think it won’t happen.

Brent’s suggestion revolves around replacing twitter status updates with RSS feeds, extending this with a published ‘following’ list, and relying on a nascent third party service(s) for searching & mentions.

The challenges:

  • RSS hosting: your feed and following list need to be hosted somewhere publicly accessible. Geeks can easily sort out hosting, ‘normal people’ are going to have to rely on blogging platforms and other services.
  • Identity: twitter enforces unique usernames (and restricts wilful impersonation); the best you can hope for with RSS is a unique URL. Impersonation is likely to be an issue.
  • API: posting twitter updates from third party applications involves a single API endpoint, a consistent authentication mechanism, and a clearly documented posting API. Distributed feed hosting will make it difficult for third-party applications to post.
  • Front end: I suspect a single, canonical, user-friendly web interface is more important than is immediately obvious. Certainly the service would struggle to establish acceptance and mindshare without one.
  • Searching & mentions: A complete solution is effectively going to involve indexing every RSS feed on the internet, in real time. This will beget considerable storage and bandwidth costs, let alone the engineering expertise required to build it.
  • Governance: search, mention and other interactions involve consistent application of mutually agreed logic. In a distributed scenario, a working group or standards body would need to publish & enforce the protocol.

None of these challenges are insurmountable, but I doubt if any can be properly overcome without the resources of a well-funded (i.e. commercial) organisation. Indexing/searching in particular is going to be expensive. A successful service is going to provide hosting, identity, a good API, value-adding development etc — which will need to be funded somehow — at which point you have another twitter.

My belief is that twitter as it currently exists is the result of innumerable obvious & non-obvious market forces, and despite many people desiring otherwise, it’s the optimum solution based on the current circumstances. That’s not to say it can’t be disrupted by a different model in the future (and it’s certainly incumbent upon us to explore this, as Brent has, in the hope we’ll find it), but I don’t think this is it.

Follow

Get every new post delivered to your Inbox.