I’m currently writing this post on a newly acquired Asus 1000H mini-notebook. I have to say that, so far, I’m really impressed with the device. The first thing I’ve noticed is the weight - it weighs about 1.2 Kg. It has a nice screen (10 inches), that has a maximum resolution of 1024x600. However, if you connect the device to an external monitor, it is capable of doing 1280x1024. On top of the screen there’s also an integrated 1.3 megapixel camera for video conference.
I’ve looked at several mini-notebooks and most ones have the sound speakers to the sides of the screen or the keyboard, which reduces screen size or requires smaller (or less) keys. The 1000H has the speakers on the underside of the case, facing the user. The keyboard is a 92% keyboard, meaning that the keys are just a bit smaller and has less keys than a regular laptop keyboard. However, after a few days writing on this keyboard, I’m almost up to my usual productivity level (I just have a hard time hitting the right Shift key).
There is so much more about this equipment that I’m really enjoying. It comes with a 160Gb SATA drive and a 10Gb SSD (solid state drive) built in. It features a stereo microphone and a multi-touch track pad. It has Bluetooth and a wireless-B,G, draft N network card. The main feature is, of course, the Atom 1.6 GHz processor, which makes the six cell battery pack last for over five hours. It has an SD card reader, a VGA-out connector, three USB 2.0 ports, an Ethernet card and a Kensington lock port.
The device comes with 1Gb of RAM, but I upgraded it to 2Gb. I’m running Windows Vista Business edition, with Office 2007 Ultimate. I’m using the notebook for meetings and note taking, for mail, editing some documents, doing presentations, and running Excel. I think this covers most professional uses. With the present configuration, the device gets a 2,8 Windows Experience Index (just due to the 3D acceleration capabilities of the graphics card). However, Windows runs with Aero glass smoothly and the overall experience is great (after a few tweaks).
When I’m at the office, I have the notebook connected to an external monitor with an USB Keyboard and mouse. It’s a really great, highly portable work machine. Not for the power user, or hard core developer. Also not for video editing or gaming. Strictly for office use – documents, spreadsheets and the occasional web surfing.
The main thing with this device is, of course, that the price is unbeatable. It’s a really low price for a machine with these characteristics. It’s a great value for money.
I absolutely love software development. And what I love the most in software development is it’s creative aspect. I believe there is not one developer in the world who hasn’t yet experienced, at least once, an “eureka” moment. You know, those insights that you get after hours or days spent thinking about a problem or trying different approaches. Suddenly, the solution just pops into your head. You run to the keyboard and hammer at it frantically. You build your solution, deploy it, execute and… It all works! Just like that. Just like magic. As if it never had been any other way. If anyone has any doubts about whether software development is a creative process, just think about this for while. This process, that happens every so often, is also evidence of how our mind works.
The anatomy of our minds
The human brain is divided into two hemispheres. The left hemisphere is responsible for analytical and logical thinking while the right hemisphere is responsible for emotional and creative reasoning. Neuroanatomist Dr. Jill Bolte Taylor, defines the right hemisphere as a “parallel processor”, receiving input from all our sensory interfaces and caring about nothing else other than the present moment, and the left hemisphere as a “serial processor” thinking linearly, analyzing and categorizing all the sensory data received by the right hemisphere. In a sense software developers have a tendency to be “left-brain thinkers”, people who are analytical, factual, logical and rational by nature, while designers, musicians and other artists tend to favor the use of the right hemisphere which enables them to tap into their creative and emotional ego.
Why creativity matters…
Software developers are faced daily with project constraints and restrictions. Deadlines and ship dates are often set, dependant components are not ready on time for integration, new features creep into the spec and so many other issues arise during the process of developing software. Just imagine having to develop a piece of software that you know will take 40 hours and you have to have it developed, unit tested, integration tested and documented in 20 hours and no one else can be assigned to help you. Sounds familiar?
Whenever we are faced with some form of constraint or hardship, our rational, logic thinking is tested against these constraints and it sometimes doesn’t hold up. Suddenly the task at hand seems impossible. In particularly challenging situations our left-brain tends to shutdown or simply go blank. In these situations the right-brain kicks in – we get frustrated, angry and stressed. The right-brain is responsible for emotional responses but it is also responsible for creative thought and playful problem-solving. Constraints and hardship trigger creative thinking and, more often than not, it’s precisely in these situations that we find so-called “creative solutions” to seemingly impossible problems – our “eureka” moments.
The creative process
In 1926, Graham Wallas, a social psychologist and educationalist, presented one of the first attempts to model the creative process in what came to be known as the Wallas stage model. This model explains the creative process in five stages (sometimes four stages where Intimation is a sub-stage):
- Preparation – Focusing on the problem and exploring the problem’s dimensions;
- Incubation – The problem is internalized into the unconscious with no external evidence of activity. You’re not even aware of thinking about the problem;
- Intimation – You somehow feel you’re on the verge of something, but it still seems elusive;
- Illumination/Insight – The idea burst into awareness. This is the “eureka” moment. This is when you run to the computer;
- Verification – The idea is applied and verified. This is the moment you test your idea and see it working.
Other models may exist. Some different, some adapted, but according to my own experience this one comes pretty close to the different stages I go through.
Tools of the trade
Everyone is creative, but as adults we usually shy away from admitting it (people in a science or a business profession more than most). Sir Kenneth Robinson says that “if you walk into a room full of five year olds and ask “Who can draw?”, all hands go up. If you ask the same question in high-school almost no hands go up”. Sir Kenneth Robinson, suggests that we unlearn to think creatively. I have to agree. However, don’t believe anyone who says “I don’t have a creative bone in me.” it’s absolutely not true – after all no one can survive on only half a brain. The good news is that we can relearn to think creatively. There are several tools and techniques that are designed to stimulate creative thought and help solve problems creatively. The list below gives an idea of such techniques but is by no means complete:
What about software development?
There are many stages in the software development lifecycle where these techniques and tools can be used. However, providing a recipe is a left-brain activity and I’d suggest you try to come up with creative ways in which to use these tools. Here are a few ideas of when these tools and techniques can be used:
- Use mind-mapping to capture requirements and while talking with customers and users;
- Use brainstorming sessions together with mind-mapping to capture design ideas;
- Whenever you get stuck, just take a step back, relax, go do something else. You’re already aware of the problem. This is the incubation period, so just wait it out – part of your brain is still actively working on the problem (hmm… Can we still bill these hours to the customer?);
- If you get into what seems to be an impossible situation with no solution in sight, try to think outside the box using lateral thinking. This can be done effectively in brainstorming sessions as well.
- In the end, always verify that the ideas you came up with are feasible (back to the left brain!)
In the end we’re all trying to make great software, and what makes software great is it’s applicability to people’s lives. Creativity generates new ideas. The application of new and creative ideas is called Innovation…
Guy Kawasaki is a venture capitalist and one of the greatest public speakers in our industry. From his experience as a venture capitalist Guy’s written a very interesting post advising young entrepreneurs on how best to start their software company.
So, if you’re planning on starting up your own company, here’s a piece of advice from someone who has surely seen the raise and fall of many potential great companies.
As a consultant part of my job is to provide guidance to our customers on the best way to use our technology, namely in terms of application development practices. Over time, you start to better understand the type of customer you’re working with, what works, what doesn’t and you start to optimize your own delivery processes. You reuse material and refine it the next time around. That’s the nature of our work in the Services organization.
Lately I’ve been involved in several engagements in the Application Platform Optimization space, mainly the Application Lifecycle Management suite of Offerings.
At the present I’m working with one customer who has gone through the Assessment phase and we’ve arrived at a six phases maturity growth roadmap that they’re starting to implement. One of the key aspects of this type of custom engagement is that all the experience gained by the customer and all the guidance delivered by the consulting team, should be persisted by the customer. We’re working with a small team within the customer and they are the ones who will have the responsibility of creating their own guidance and internal best practices and sharing all this information with the rest of the organization. So to collect, persist, distribute and, later on, update their in-house customized guidance for application architecture, development and lifecycle management, we’ve chosen to use the Guidance Explorer tool available on Codeplex.
The use of this tool turned out to be a great fit for this scenario. Instead of compiling one giant document, that would go through several versions and would quickly fall out of synch, the customer is now publishing and updating guidance, based on an existing database of industry and technology best practices. Guidance articles have their own metadata, the lifecycle of each piece of guidance is independent of each other and, if needed, all the guidance can be compiled into a Word document for a hardcopy reference. Additionally, one of my favorite features is the ability to subscribe to the guidance through a published RSS feed and receive a notification whenever guidance changes or a new best practice is published.
The Patterns & Practices team is finally going to update their fantastic Application Architecture Guide. Since it’s release in 2002 this guide has been a favorite of mine. It’s somewhat dated but it contains sound design principles that stand the test of time.
Now the team is working on V2, which is planned for the first half of 2009. You can follow their work through the Application Architecture 2.0 Knowledge Base on Codeplex.
You can also follow the work and additional discussions on the subject through J. D. Meier’s blog.
I believe this is a great piece of guidance and am sure it will help shape the near future of application and service architecture on the Microsoft platform.
Many of us in the field have come across heterogeneous source control environments within the same organization. Some organizations, having multiple source control systems, wish to have some way of synchronizing TFS with their other systems. I've seen this when development teams who are used to the Microsoft development tools tend to prefer Team Foundation Sever and sometimes operations teams, who are responsible for deploying and maintaining the software produced internally maintain a portfolio of versions of the software in other control systems, like ClearCase for instance.
The TFS Migration and Synchronization Toolkit allows developers to build custom tools that provide migration and synchronization capabilities with other version control systems.
There's a new version of this toolkit available in Codeplex. This release includes enhanced support for the migration of branches and merges.
Dr. Laurie Williams has been a long-time researcher on Agile methods and their impact on software engineering.
She and her students have published a number of very interesting papers on varied subjects, particularly on the economics of agile practices, like Test Driven Development (TDD) or Pair Programming.
You can find a list of publications here.
I have always been fascinated by building design and architecture. My brother is an architect and we have long talks on the subject, where we discuss not only the works of particular architects or artists, but the nature and profession of building design. I find these talks stimulating because I can relate to most of these topics as well.
When it comes to software, however, I find the role of the architect not as defined as it is in construction. At least not yet, as there seems to be a lot of discussion going on about the nature and role of an Architect. The IT industry is not just about software, it's also about the enterprise, the infra-structure and so on. In IT we find infra-structure architects, solution architects and enterprise strategy architects, each focused on one type of architecture. There is one other aspect that, in my mind, widens the gap between architecture in software and in construction, is that architecture in construction is more akin to art than to science, while architecture in IT traditionally stems from engineering and is deeply rooted in science.
So, regarding architecture in IT I keep asking myself two questions:
- Is software architecture art or science?
- With so many software engineers available how do we articulate the value of and need for an architect?
I won't address the first question here as it is somewhat metaphysical (not to mention controversial) and is food for another post at a later time. I have to admit I don't have a clear answer to either of these questions, but I venture that to find an answer (if an answer is to be found) we should look at the meaning of architecture in construction and in building design.
In the conversations I've had with my brother around architecture, I became distinctly aware of the great importance architects place on context: topological context, social context, design context, material context.
Take a look, for instance, at the work of architects like Tadao Ando, Mies Van der Rohe, Frank Lloyd Wright or even Frank Ghery and you can see and feel how their buildings either are inserted into the context of the surrounding landscape or they provide a new context to the surrounding landscape. Even when the buildings are disruptive, they are so contextually (i.e. the disruption provides a new context to the surrounding elements).
I venture that one of the greatest values of a software architect's to an IT organization is to provide software design in context. With this I mean, the right design that satisfies a number of propositions, those being the requirements for the software, but also the context in which that software is to be built and operated. This context can be defined in many different ways, for instance tying a business goal to IT capabilities and mapping technology to those capabilities or understanding a services strategy for a given enterprise and designing a LOB application according to that strategy. It might seem obvious and common sense, but I find that this context is frequently overlooked or dismissed, causing numerous disruptions, like constant feature redefinition, scope creep, cost overruns and client dissatisfaction.
In a time where IT is seen as a cost center and there is a generalized idea that most software is built like a house of cards without much structural integrity, it seems like confidence in IT as a strategic asset is dwindling. IT spending is becoming tighter and IT managers have to prove the value of IT to business stakeholders. Architects are a valuable asset to any IT organization in defining the principles for the IT eco-system (the IT context for the enterprise), helping to align business goals and IT capabilities.
Out-of-the-box Visual Studio allows testers to define a network mix for load tests with the following network profiles:
- LAN
- T3 6.0 Mps
- T1
- Cable/DSL 1.5Mbps
- Cable/DSL 768k
- Cable/DSL 384k
- Dial-up 56k
- Dial-up 33.6k
- Dial-up 28.8k
If we want to add an additional entry, say "Dial-up 128k", we'll have to add a new network profile definition file.
These files can be found in the following folder:
C:\Program Files\Microsoft Visual Studio 9.0\Common7\ide\Templates\LoadTest\Networks
Just select an appropriate file (say "Dial-up 56k.network"), copy and rename it (to "Dial-up 128k.network") and edit it's contents accordingly:
<Network Name="Dial-up 128k" BandwidthInKbps="128">
</Network>
You'll have to close all Visual Studio instances and re-open them so that it can reload all network profiles.
Related Links:
I finally got some time to setup a powershell script that mimics the vcvars32.bat file that the Visual Studio 2008 command prompt loads (if you're on a 32-bit machine) and sets up all the environment variables for using the development command-line tools.
To create your own VS 2008 PowerShell Prompt just create a text file and call it "vcvars32.ps1" and add the following content:
Write-Host "Setting Powershell environment for using Microsoft Visual Studio 2008 x86 tools."
$Env:VSINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0"
$Env:VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0\VC"
$Env:FrameworkDir="C:\Windows\Microsoft.NET\Framework"
$Env:FrameworkVersion="v2.0.50727"
$Env:Framework35Version="v3.5"
$WinSdkInstallFolder = (get-itemproperty -path "HKLM:\SOFTWARE\Microsoft\Microsoft SDKs\Windows" -name CurrentInstallFolder).CurrentInstallFolder
if ($WinSdkInstallFolder -ne "")
{
$Env:WinSdk = $WinSdkInstallFolder
$Env:Path = $WinSdkInstallFolder + "bin;" + $Env:Path
$Env:INCLUDE = $WinSdkInstallFolder + "include;" + $Env:INCLUDE
$Env:LIB = $WinSdkInstallFolder + "lib;" + $Env:LIB
}
$Env:Path="C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE;C:\Program Files\Microsoft Visual Studio 9.0\VC\BIN;C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools;C:\Windows\Microsoft.NET\Framework\v3.5;C:\Windows\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\VCPackages;" + $Env:Path
$Env:INCLUDE = "INCLUDE=C:\Program Files\Microsoft Visual Studio 9.0\VC\ATLMFC\INCLUDE;C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;" + $Env:INCLUDE
$Env:LIB = "C:\Program Files\Microsoft Visual Studio 9.0\VC\ATLMFC\LIB;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;" + $Env:LIB
$Env:LIBPATH = "C:\Windows\Microsoft.NET\Framework\v3.5;C:\Windows\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\ATLMFC\LIB;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;" + $Env:LIBPATH
To be able to execute scripts from a powershell prompt you need to set the correct execution policy for your environment. The default execution policy is "Restricted" which does not allow scripts to be run.
You can choose to set the execution policy to allow signed scripts to be run or to disable it altogether (usual disclaimer: This option will allow untrusted scripts to be run, so make sure you know the implications and choose wisely...)
To set the execution policy you can use the command:
Set-ExecutionPolicy -executionpolicy <policy>
To sign the script, you must go through the steps described by the following article:
Get-Help about_signing
All that's left is to create a shortcut that runs your "vcvars32.ps1" script at startup. So, just create a new shortcut and set the following Target property:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -File <path to your "vcvars32.ps1" file>
PowerShell is a great tool for developers. Hope this helps you take advantage of it.
Ever wondered how to configure a connection string using the Data Link properties dialog?
Well, I have. And just last night I decided to check out exactly how to do this. Here's a simple list with all the required steps:
- Add a reference to "Microsoft OLE DB Service Component 1.0 Type Library" (it’s in the COM Tab)
- Add a reference to the Primary Interop Assembly (PIA) for ADO (Add References > adodb)
- Add the following code (for instance to a button click event handler):
1: ADODB.Connection conn = new ADODB.Connection();
2: object oConn = (object)conn;
3:
4: MSDASC.DataLinks dlg = new MSDASC.DataLinks();
5: dlg.PromptEdit(ref oConn);
6:
7: txtConnectionString.Text = conn.ConnectionString;
We can also pre-assign the ConnectionString property of the Connection object before we open up the Data Link dialog.
If we do this, the dialog is already filled with the configuration from the connection string.
Under Software Configuration Management, branching and merging are common features that help maintain stable code bases and scale the development effort by synchronizing parallel development. However, depending on the size of your product, the size of your team, your isolation requirements and your development cycle, parallel development can become quite overwhelming, so it's very important to define a good strategy for branching and merging that meets your needs. This isn't always straightforward, so here are a couple of guidelines I usually use to tackle this issue.
The first thing I try to find out are the isolation requirements for parallel development, how many development teams there are and what are the stabilization protocols (how many quality gates there are and how many different test environments there are where these gates are checked).
Having this information helps me to define the logical structure for the branching strategy as well as the physical source control structure to support it.
One thing I always have to look out for, is to make sure that the logical structure is applicable to the physical structure. For this I usually define a set of use cases that act as a model that I "run" on both the physical and logical structures to check if they are supported.
Here are a couple of scenarios I usually contemplate:
- Fixes
- Release Stabilization
- Code Promotion
I try to check all of these scenarios, considering that a mix (or all) of them might occur simultaneously. Here's a possible scenario I try to work out:
"Imagine there's a team working on a new release of feature A, part of another team is working on a fix of a previously released version of their feature (feature B) while the rest of the team is working on the next version. In the meanwhile, a third team is required to release feature C to a new test environment for functional testing by end-users."
Some questions that I might ask for this scenario:
- How many teams/features are there?
- What should the physical structure of my source control repository be?
- How many independent branches do I need to make this scenario valid?
The Branching Guidance by the P&P team is an excellent place for different approaches to these questions and many more.
So, what do I really look out for when defining a branching strategy?
- Good Logical Structure
- Good Physical Structure
- Make sure the logical structure is applicable to the physical structure
- Run a model on both the logical and physical structures.
I've added WiX to my build process to automate the production of windows installer packages for my product. I'm not very proficient with Windows Installer technology, so I thought I might get by with what I've seen is already a "traditional" approach: dark an MSI created by a Visual Studio Setup Project. Well, I knew that this was not the right approach the moment I laid eyes on the script file I got from the decompiler.
However, it was sufficient to familiarize myself with the general structure of a WiX script. Soon after I decided to drop that script and start fresh. After a few dead-ends (that's when I used to refer to the Windows Installer SDK) I got my script working.
My thoughts on the subject are:
- Don't rely on the script you get from dark. It's confusing and things like setting up a virtual directory on a web server can be done with the WiX built-in custom actions in a very simple way;
- Debugging isn't easy and most of the time errors are unclear (However, from Rob's brownbag video, I suppose this is only temporary as there are plans to hook the WiX compiler to Visual Studio);
- The WiX documentation (for the schema) and the Windows Installer SDK will be your best friends while writing the script, so keep them handy.
Using Managed Custom Actions with WiX
Incorporating managed Custom Actions in WiX is a relatively simple task. However, you have to pay attention to a few details to avoid ending up with a headache just trying to figure out what's wrong with the script.
The first thing I did was making sure that the installer class was in the package, so I added a file reference for the assembly with the installer type.
1: <File Id="InstallerFile" Name="SAMPLE~1.DLL" LongName="SampleInstaller.dll" src="$(var.BinFolder)\SampleInstaller.dll" Vital="yes" KeyPath="yes" DiskId="1" />
What's worth noting here is the specific Id for the file. I did this because it was necessary to reference that specific file later in the script, otherwise I just stuck with the Ids I got from tallow (for the rest of the files in the package).
To setup WiX to execute a managed installer class as a Custom Action, I used 'InstallUtil'. This tool can be run from the command-line ('installutil.exe'), but what I really wanted to use was 'InstallUtilLib.dll'. In order to do this I added the following reference to the script:
1: <Binary Id="InstallUtil" src='$(var.BinFolder)\Installer Assemblies\InstallUtilLib.dll' />
If you've ever darked (decompiler in the WiX toolset) a VS Setup Project that uses managed custom actions, you're probably familiar with the following script entries:
1: <Binary Id="VSDNETCFG" src="Binary\VSDNETCFG.ibd" />
This entry is used to produce a variable that is passed as part of the command-line arguments of installutillib. The variable is [VSDFxConfigFile] in the darked script.
Upon close inspection this embedded file (VSDNETCFG.ibd) is a simple config file that provides run-time binding information. So, I just added it to the installation package:
1: <File Id="ConfigFile" Name="SAMPLE~2.XML" LongName="SampleInstaller.dll.config" src="$(var.BinFolder)\SampleInstaller.dll.config" Vital="yes" DiskId="1" />
Setting up the Custom Action is simple, albeit not trivial. The Custom Actions are calls to installutillib. My advice is to always check these using "InstallUtil.exe" via the command-line, to make sure they're working ok.
1: <CustomAction Id='Uninstall' BinaryKey='InstallUtil' DllEntry='ManagedInstall' Execute='deferred' />
2: <CustomAction Id='UninstallSetProp' Property='Uninstall' Value=' /installtype=notransaction /action=uninstall /LogFile= "[#InstallerFile]" "[#ConfigFile]"' />
3:
4: <CustomAction Id='Install' BinaryKey='InstallUtil' DllEntry='ManagedInstall' Execute='deferred' />
5: <CustomAction Id='InstallSetProp' Property='Install' Value=' /installtype=notransaction /action=install /LogFile= /BinDir="[TARGETDIR]\" /Package="[ProductName]" "[#InstallerFile]" "[#ConfigFile]"' />
6:
7: <CustomAction Id='Rollback' BinaryKey='InstallUtil' DllEntry='ManagedInstall' Execute='rollback' />
8: <CustomAction Id='RollbackSetProp' Property='Rollback' Value=' /installtype=notransaction /action=rollback /LogFile= "[#InstallerFile]" "[#ConfigFile]"' />
9:
10: <CustomAction Id='Commit' BinaryKey='InstallUtil' DllEntry='ManagedInstall' Execute='commit' />
11: <CustomAction Id='CommitSetProp' Property='Commit' Value=' /installtype=notransaction /action=commit /LogFile= "[#InstallerFile]" "[#ConfigFile]"' />
There are a few particulars to note in the script fragment above:
- The values of the properties are passed to the custom action due to the fact that the Id and Property attributes match (so, make sure they do).
- Make sure that the last parameter is the config file mentioned before. If this file cannot be found you'll probably get the following error:
InstallUtilLib.dll: Unknown error in CorBindToRuntimeHost (0x80131700).
- If you wish to pass some data to your custom action, like the parameters /BinDir or /Package in the install Custom Action above, make sure that if you're passing some Windows Installer property that returns a directory you enclose it in quotes and include a trailing backslash (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vxgrfcustomactiondataproperty.asp). If you don't, you might find yourself struggling with the following error:
System.IO.FileNotFoundException: File or assembly name <assembly>, or one of its dependencies, was not found.
After defining the Custom Actions all that's left is to sequence them in InstallExecuteSequence:
1: <Custom Action="InstallSetProp" After="StartServices">$SampleComponent>2</Custom>
2: <Custom Action="Install" After="InstallSetProp">$SampleComponent>2</Custom>
3:
4: <Custom Action="UninstallSetProp" After="MsiUnpublishAssemblies">$SampleComponent=2</Custom>
5: <Custom Action="Uninstall" After="UninstallSetProp">$SampleComponent=2</Custom>
6:
7: <Custom Action="CommitSetProp" After="Rollback">$SampleComponent>2</Custom>
8: <Custom Action="Commit" After="CommitSetProp">$SampleComponent>2</Custom>
9:
10: <Custom Action="RollbackSetProp" After="Install">$SampleComponent>2</Custom>
11: <Custom Action="Rollback" After="RollbackSetProp">$SampleComponent>2</Custom>
In the end I was surprised at how simple, compact and clean I got my script, especially when compared to the one I got from dark. On another note I have to say that the WiX built-in Custom Actions were a huge help.
During TechEd'04 in Amsterdam I attended a Birds Of a Feather session on how to integrate unit testing into the Software Development Life-Cycle. For those who are unfamiliar with the format of these sessions, it's a speaker moderated discussion, where we can ask questions and have them answered and commented by the speaker and other attendees. I enjoyed this session format very much as it's highly interactive.
This session was moderated by James Newkirk. The audience placed a few questions that led to various discussions on unit testing and the correct way to do them (or not to do them), unit testing techniques and personal experiences both positive and negative when using these techniques.
Below are the topics discussed during this session and I thought of placing them here, along with some of my personal impressions on the various subjects.
- Testing abstract classes with different implementations
How to test a virtual implementation of a class, when different implementations can spawn at any time ?
James Newkirk was of the opinion that you should abstract the tests when a new implementation appears.
My opinion on the subject follows along the same lines. If I'm doing TDD, I want to write test code for all my implementation (this is my opinion on testing privates as well, BTW). So, I'm a firm believer in testing the implementation not the contract, because that's where my logic will be. It's also my opinion that I should code my unit tests just like I do my production code. So, when there's the need to do some refactoring in my unit test code, I'll do it. This includes doing an Extract Interface, or Extract Class, or any other abstraction refactoring.
- How much to test / testing privates
This is a topic that has always had strong arguments pro and against as was also evident in this session. While James Newkirk's opinion is not to test privates, I'm not so sure we shouldn't. James doesn't like to test the private methods, because it hinders refactoring. But if you don't have a test for a piece of logic, how can you refactor it (this seems like a catch 22 to me) ?
As I've stated above I'm not so sure we shouldn't test private members. I guess that this depends on how I do TDD. There are two different approaches here. Write code test-first only considering the publicly visible members (or the public interface), or write tests for all your logic and during refactoring, if a given method turns out not to be used by any other class other than the test class, use the Hide Method refactoring. I use this last approach, for two reasons:
- If I'm only coding tests for the public interface, I'll have no idea how much logic I'll be having "under the hood" or beneath the public interface. But typically, as someone pointed out during this discussion, this is were most of the logic will be;
- The other reason is that this allows me to code this (soon to be) "internal" logic, using a test-driven approach, which gives me confidence in the implementation and how I refactor it.
This approach, however, leaves me with a bunch of orphaned test methods, since they can no longer access the methods they should be exercising. This is when I usually refactor the test code as well, replacing method invocation with reflective method discovery and invocation.
- Legacy code w/o tests
The scenario is the following. You have a large code-base for which you don't have any unit tests. You'd like to add unit tests, but where do you start ?
James answered this question in a very pragmatic way IMO. "Don't add unit tests for anything you aren't about to change." He said.
Well this seems a very sensible decision. He then elaborated on this by stating that it could be done by establishing a virtual "boundary" of unit tests defined by the things about to be changed. Tests can be written against this "boundary" and the changes made on the inside of the boundary.
- How to convince people to code test-first?
I see this issue as a very hot topic. Primarily because, I have been trying to convince people to adopt this way of developing. And I know from personal experience that it's hard to find the right arguments. Strangely enough, I've found that direct management (PMs or Dev Leads) are more receptive to the message of TDD than the actual developers.
James Newkirk stated that developing software "...is not about the individual developer, it's about the team and the continuous maintainability of the code". Someone also pointed out that "blinking lights changing from red to green help a lot!" I couldn't agree more.
Additionally, James pointed out a "technique" that I've also used with some success, which is to ask someone to test their own code. Most of the times people find it hard to write tests against their own code, because they usually don't write their code to be tested.
This debate spawned another thread of discussion. If the developers write unit tests, which was usually a big chunk of the work that was done by QA teams (white-box testing), the QA teams must start testing at a different (higher) level.
- Mock Objects
Just like Benjamin posted, "James described mock objects as the situation where object A interacts with objects B and you need some way of 'switching out' Object B to create a 'dummy response' from the calls of Object A."
True, but it's necessary to determine why would we want to 'switch out' Object B. I usually 'switch out' object B if it isn't part of my domain code (maybe it's an object belonging to a third-party API). If my Object B is, for instance a DAL component and it uses ADO.NET to connect to the database, I would use (dynamic) mock objects to replace the ADO.NET's data access components in object B using the "Inversion of Control" pattern. It may be somewhat complex, but it promotes decoupling and testability, which rate very high in my trade-off scale, so I usually think it's worth it and end up implementing it.
- Testing against a DB.
Sometimes it's hard to mock out the data access layer components, someone pointed out. This is true and there's usually a tendency to end up testing against the database.
Actually, James Newkirk says that there are situations where he prefers to test against the database. "When writing unit tests for DAL components use the actual database. When writing unit tests for components that use DAL components, mock out the DAL components." James said.
I have a different point of view. If the DAL components where written by me, I'd like to test not only the components that use the DAL components, but also the interaction between them. On the other hand, data connectivity components supplied by the underlying API (system or third-party) are not part of my domain code and I can (and probably should) mock them out.
- How long should tests run ?
This was an interesting point, as people have very different experiences. Someone said they had test that took 6 hours to complete, others have them built into the build process and run the whole suite nightly for as long as they take. Another delegate's tests took about 2 hours to complete.
In James' opinion, 1 hour is too long, because tests should provide rapid feedback to the developer and promote a very fast turn around in the development lifecycle. The recommendation that came out of this debate was that in large projects we should separate developer unit tests and run all the tests (all test suites from all developers) during the daily build cycle. Some integration problems will arise only during build, but it's a trade-off.
- Automatic test generation.
Commercial products don't take into account the actual intention of the methods being tested and so usually create 'poor' tests. On the other hand, Visual Studio Team System has a built-in functionality to implement a boiler plate test for any given method.
- How do you unit test GUIs ?
In any discussion on unit testing this topic usually comes up. In this session people mentioned some commercial products that they've used with some success, although they couldn't survive for more than a few builds without breaking them. A pretty "standard" response to this issue is to use the Model View Controller design pattern to separate presentation and presentation control logic and eventually test them individually.
The problem is that when we're coding unit tests against an API, the interface is programmatic, whilst when we are coding unit tests for a GUI, the interface is visual and designed for human and not machine interaction.
I've used NUnitForms and NUnitASP with some success to get around this situation and exercise the visual elements of the GUI individually. The main drawback with both of these extensions to NUnit is the relatively small number of controls supported (granted that they are the most used).
Well, a few other questions and discussions came up, but were marginal to the previous ones that were the main thread of the session.
Usually one of the major difficulties a developer faces when writing unit tests is how to write test code for User Interfaces. This is particularly important when we're doing Test-Driven Development.
The Visual Studio .NET IDE makes it difficult to unit test Windows Forms applications, because if we want to keep the test code decoupled from the UI code (which, in this case, is our domain code), we should keep the test code in a separate assembly and reference the assembly that contains all the forms. The IDE doesn't allow us to reference an assembly that's compiled to an .exe. Although this is only a limitation of the IDE, there is a very effective way to structure the solution so that we can develop UI code test-driven and keep the test code in an isolated assembly.
In fact, we only try to reference the Windows Application assembly, because it's where the IDE's code generator places the forms in the first place. However, we can just as easily setup a Class Library assembly and move all the forms to this assembly. All we need to do is to set up a reference from the Windows Application assembly to the Class Library assembly.
I usually setup the project structure of the solution in Visual Studio like this:
- Class library for the user interface
- Windows Application with the application launcher (Main() method)
- Class library for the unit tests
Some things in the previous list are worth noting. First, both project 2) and project 3) should set a reference to project 1), which is where all forms should be developed.
Second, project 2) will be the default startup project and will only have the Main() method that runs the startup form from project 1).
To develop the unit tests I use NUnitForms, which is an extension to NUnit that allows me to create an instance of a form and manipulate the controls in it.
Here's a (really) simplistic example of what a unit test might look like:
[Test]
public void TestOnButtonPressed()
{
MyUserInterface controller = new MyUserInterface();
controller.Show();
TextBoxTester textBox = new TextBoxTester("txtName");
textBox["Text"] = "John Doe";
ButtonTester button = new ButtonTester("bttnHello");
button.Click();
LabelTester label = new LabelTester("lblName");
Assertion.AssertEquals("Hello, John Doe!", label["Text"]);
}
The test code creates an instance of the form (possible because we can reference the Class Library that has the form) as well as some objects (from NUnitForms) that I can use to control each of the form's controls (the button, the label and the textbox). It assigns the value "John Doe" to the "Text" property of the textbox control, fires the button's click event and checks if the label's "Text" property is the expected salutation string.
If I'm writing this code test-first, the previous code gets me to a red bar in NUnit, so I have to code the handler for the onClick event for the button.
private void bttnHello_Click(object sender, System.EventArgs e)
{
lblName.Text = "Hello, " + txtName.Text + "!";
}
A quick compile and I run the test suite in NUnit and I get a green bar this time.
Of course this is a completely simplistic example, but since I can create an instance of the form and interact with its controls from my unit tests, I can implement whichever behavior is required for the user interface.
Update!
[31-03-2005]
You can download a working sample here (94Kb, zip).
Just be sure to update all the references and reference paths in the solution to the locations where you have NUnitForms installed.