Today, we have commited in the Gallio development trunk, the new syntax for the MbUnit contract verifiers.
That new syntax is a major breaking change. After you update Gallio with the latest version (3.0.5.589 and later), you will need to modify the declaration of your contract verifiers to make your project compilable again. Download the latest development build here.
Please refer to that post for additional details about the new syntax.
2008/11/18
Announcing Gallio and MbUnit v3.0.5
Jeff Brown has announced today that Gallio and MbUnit v3.0.5 are in the wild.
Download here: http://www.gallio.org/Downloads.aspx
Documentation here: http://www.gallio.org/Docs.aspx
Release notes: All release notes from all versions.
Download here: http://www.gallio.org/Downloads.aspx
Documentation here: http://www.gallio.org/Docs.aspx
Release notes: All release notes from all versions.
2008/10/17
New Assertions for MbUnit v3
[Update 11/12/2008: As explained in a comment below, Jeff has restored the delta equality assertion with a set of methods named Assert.AreApproximatelyEqual.]
The latest version of MbUnit v3 which comes with Gallio v3.0.4, has a completely new set of assertions methods. It has been entirely re-written by Jeff and Vadim. The new MbUnit.Framework.Assert class is indeed more consistent. It makes an intensive use of generics and lambda expressions too.
Of course, it comes at the cost of some breaking changes, because many methods have been removed or renamed. But the new assertion methods are very powerful and much more flexible than before; so those breaking changes are easy to fix.
For example, the following method has been discarded:
The latest version of MbUnit v3 which comes with Gallio v3.0.4, has a completely new set of assertions methods. It has been entirely re-written by Jeff and Vadim. The new MbUnit.Framework.Assert class is indeed more consistent. It makes an intensive use of generics and lambda expressions too.
Of course, it comes at the cost of some breaking changes, because many methods have been removed or renamed. But the new assertion methods are very powerful and much more flexible than before; so those breaking changes are easy to fix.
For example, the following method has been discarded:
Assert.AreEqual(double expected, double actual, double delta)
The delta parameter specified the maximum acceptable difference between the expected and the actual values. That assertion was useful when comparing the result of a calculation involving potential rounding issues. The following assertion was passing because 3.14 and 3.14159 differs less than 0.01.
Assert.AreEqual(3.14, 3.14159, 0.01);
The latest version of MbUnit proposes to use that new generic assertion method instead.
Assert.AreEqual<T>(T expectedValue, T actualValue, Func<T, T, bool> comparer)
The signature of the method seems much more complicated, especially if you are not familiar with generics. However, thanks to the C# 3.0 type inference, you do not need to specify explicitely any type parameter. So in fact, you can write the following line, which has the same effect has the example above.
Assert.AreEqual(3.14, 3.14159, (x, y) => Math.Abs(x - y) <= 0.01);
2008/10/16
2008/10/06
Rhino.Mocks 3.5: The new generic list constraint.
Oren Eini has finally released Rhino.Mocks 3.5 RTM. As written in Oren's article (many thanks to him for the credits), I had submitted some time ago a patch, which adds a new useful constraint for generic collections. Some explanations are perhaps needed:
Suppose that you are setting up some expectation on a method that takes a collection as parameter: a list of strings, for example.
Suppose that you are setting up some expectation on a method that takes a collection as parameter: a list of strings, for example.
public interface IFoo
{
bool DoSomething(List<string> values);
}
With Rhino.Mocks 3.4, you can specify a constraint on the input collection. For instance, you can verify that the fifth element of the collection has the value "Hello".
Expect.Call<bool>(mockFoo.DoSomething(null))
.IgnoreArguments()
.Constraints(List.Element(4, Is.Equal("Hello"))
.Return(true);
Unfortunately, the List.Element method can only take an Int32 as first parameter. Thus, it makes the constraint only usable with Int32-indexed collections, such as lists and arrays. It is a shame if your parameter is a dictionary.
That's why I have written a new abstract constraint (KeyedListElement) and overriden the List.Element with a new generic method. The usage is very similar. It is actually a kind of generalization of the former method.
That's why I have written a new abstract constraint (KeyedListElement) and overriden the List.Element with a new generic method. The usage is very similar. It is actually a kind of generalization of the former method.
public interface IFoo
{
bool DoSomething(Dictionary<string, string> values);
}
Expect.Call<bool>(mockFoo.DoSomething(null))
.IgnoreArguments()
.Constraints(List.Element<string>("Value1", Is.Equal("Hello"))
.Return(true);
The example above defines a constraint on the parameter (a dictionary of strings keyed by strings). The value at index "Value1" must be equal to "Hello". Note the generic signature of the method, which specifies the type of the key.
Hope it helps!
Hope it helps!
2008/09/30
Gallio + PowerShell
Nobody is perfect: I am fond of PowerShell. Yes, I know, the PowerShell language syntax is a bit weird sometimes (Do you know any other languages where you use command switches to compare things?), but despite the ugly look of the scripts, this is a very powerful tool.
Gallio has a very handy snap-in for PowerShell, written by Julian Hidalgo and Jeff Brown. It adds a very useful cmdlet which controls the Gallio test runners. The snap-in is configured by the installer. You just need to ensure that the correct install options are checked.
Once Gallio is installed, start the PowerShell console, then load the snap-in in memory with the following command:
Gallio has a very handy snap-in for PowerShell, written by Julian Hidalgo and Jeff Brown. It adds a very useful cmdlet which controls the Gallio test runners. The snap-in is configured by the installer. You just need to ensure that the correct install options are checked.
Once Gallio is installed, start the PowerShell console, then load the snap-in in memory with the following command:
Add-PSSnapIn Gallio
Then you can run your test suite by calling Run-Gallio with the appropriate arguments.
Run-Gallio MyTests.dll
More help about the arguments can be found with the following command:
Get-Help Run-Gallio -detailed
One of the most interesting features of PowerShell is that it is entirely object oriented. In that context, the Gallio PowerShell Snap-In provides a complete report object as result. The object is of type Gallio.Runner.TestLauncherResult. Sorry, no online document is available for the moment. But you can get additional details by opening the offline API documentation. There should be a shortcut copied somewhere by the Gallio installer in the start menu. [Update 11/12/2008: Online document is now available on the Gallio website.]
It is easy to look at the result properties, and make the appropriate actions. For instance, you can decide to interrupt the execution of the script if at least one unit test has failed.
It is easy to look at the result properties, and make the appropriate actions. For instance, you can decide to interrupt the execution of the script if at least one unit test has failed.
Add-PSSnapIn Gallio
$result = Run-Gallio MyTests.dll
if ($result.Statistics.FailedCount -gt 0)
{
Write-Warning "Some unit tests have failed."
Exit
}
2008/09/23
Contract Verifiers - Part IV. - The Comparison Contract Verifier
- Part I. - Introduction
- Part II. - Exception Contract Verifier
- Part III. - Equality Contract Verifier
- Part IV. - Comparison Contract Verifier
The comparison contract verifier helps in implementing correctly the generic interface IComparable. It verifies in particular that your type-specific implementation orders instances correctly.
public class Fraction : IComparable<Fration>
{
public Fraction(int numerator, int denominator)
{
// ...
}
// ...
}
As for the other contract verifiers, you must use a dedicated attribute to decorate your test fixture. And as for the equality contract verifier, your test fixture must provide classes of equivalence object instances to be consumed by the contract verifier. The only but important difference here, is that you must feed the method with ordered classes. Each class must contain instances expected to be compared strictly greater than the objects in any previous class. In other words, instances must be put in the ascending order.
[TestFixture]
[VerifyComparisonContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fration>
{
public EquivalenceClassCollection<Fration> GetEquivalenceClasses()
{
return EquivalenceClassCollection<Fration>.FromDistinctInstances(
new Fraction(1, 6),
new Fraction(1, 5),
new Fraction(1, 4),
new Fraction(1, 3));
};
}
}
The comparison contract verifier checks for the following features:
1. The CompareTo method works as expected. The method must return a negative value if the current object is less than the other object; a positive value if it is greater; or zero if they are equal. Furthermore, if the type is nullable, the contract verifier checks for null reference handling as well. Any non null instance should compares greater than a null reference; and two null references should be equal.
1. The CompareTo method works as expected. The method must return a negative value if the current object is less than the other object; a positive value if it is greater; or zero if they are equal. Furthermore, if the type is nullable, the contract verifier checks for null reference handling as well. Any non null instance should compares greater than a null reference; and two null references should be equal.
public class Fraction : IComparable<Fration>
{
public int CompareTo(Fraction other)
{
// ...
}
}
Although it is not mandatory, you might decide to override the four comparison operators. The contract verifier is able to check them too. This is an optional feature. You can disable it by setting the named parameter ImplementsOperatorOverload to false.
[TestFixture]
[VerifyComparisonContract(typeof(Fraction), ImplementsOperatorOverload = false)]
public class
FractionTest : IEquivalenceClassProvider<Fration>
{
// ...
}
2. The Greater Than (>) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator >(Fraction left, Fraction right)
{
// ...
}
}
3. The Greater Than Or Equal (>=) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator >=(Fraction left, Fraction right)
{
// ...
}
}
4. The Less Than (<) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator <(Fraction left, Fraction right)
{
// ...
}
}
5. The Less Than Or Equal (<=) operator overload is correctly implemented and behaves as expected.
public class Fraction : IComparable<Fration>
{
public static bool operator <=(Fraction left, Fraction right)
{
// ...
}
}
It is very common to implement equality and comparison in the same time. Should this happen, you can combine several contract verifiers into the same test fixture. This is very handy.
public class Fraction : IComparable<Fration>, IEquality<Fration>
{
}
[TestFixture]
[VerifyComparisonContract(typeof(Fraction))]
[VerifyEqualityContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fration>
{
// ...
}
2008/09/03
Contract Verifiers - Part III. - The Equality Contract Verifier
- Part I. - Introduction
- Part II. - Exception Contract Verifier
- Part III. - Equality Contract Verifier
- Part IV. - Comparison Contract Verifier
The path that leads to a proper implementation of the equality contract has many pitfall traps. Fortunately, the equality contract verifier will help you in avoiding them. For convenience, we will consider as an example, a hypothetical class representing a simple mathematical fraction which implements IEquality<T>.
public class Fraction : IEquality<Fraction>
{
public Fraction(int numerator, int denominator)
{
// ...
}
// ...
}
In order to test the type implementing the generic equality interface, the contract verifier needs some valid type instances. That's why the test fixture must fulfill a two side parts contract.
- It must have the attribute VerifyEqualityContract.
- It must implement the interface IEquivalenceClassProvider<T>, which will provide the test runner with various object instances to play with.
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
[TestFixture]
[VerifyEqualityContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fraction>
{
public EquivalenceClassCollection<Fraction> GetEquivalenceClasses()
{
return EquivalenceClassCollection<Fraction>.FromDistinctInstances(
new Fraction(1, 2),
new Fraction(2, 3),
new Fraction(4, 5));
}
}
The IEquivalenceClassProvider<T> has a single method that you must use to return a collection of equivalence classes for the evaluated type. An equivalence class is a collection of object instances that are expected to be equal (The notion of equality is of course to be understood in the sense of your own equality contract). In the simplest scenarios, it is generally sufficient to provide several distinct object instances to be compared together. Use the factory method FromDistinctInstances to construct such a collection of equivalence classes. In the previous example, we provide 3 equivalence classes, each containing 1 single object.
However, in more complex scenarios, we want to verify that different object instances "representing" the same value are actually equal. For instance, the fraction 1/2 should be equal to the fraction 2/4 or 3/6. Instead of using the factory method, use the constructor to provide some equivalence classes containing a variable number of objects.
However, in more complex scenarios, we want to verify that different object instances "representing" the same value are actually equal. For instance, the fraction 1/2 should be equal to the fraction 2/4 or 3/6. Instead of using the factory method, use the constructor to provide some equivalence classes containing a variable number of objects.
[TestFixture]
[VerifyEqualityContract(typeof(Fraction))]
public class FractionTest : IEquivalenceClassProvider<Fraction>
{
public EquivalenceClassCollection<Fraction> GetEquivalenceClasses()
{
return new EquivalenceClassCollection<Fraction>(
new EquivalenceClass<Fraction>(
new Fraction(1, 2),
new Fraction(2, 4),
new Fraction(3, 6)),
new EquivalenceClass<Fraction>(
new Fraction(2, 3),
new Fraction(4, 6)),
new EquivalenceClass<Fraction>(
new Fraction(4, 5)))
}
}
Now that we have defined our contract verifier, let's see what it will check for.
1. It verifies that the method GetHashCode returns the same value for equal objects. You should override the method and return an identical value for all the objects located in the same equivalence class. However, the contrary is not true: it is not required that the resulting hash value for unequal objects be different (although it would be a good idea, for efficiency reasons)
1. It verifies that the method GetHashCode returns the same value for equal objects. You should override the method and return an identical value for all the objects located in the same equivalence class. However, the contrary is not true: it is not required that the resulting hash value for unequal objects be different (although it would be a good idea, for efficiency reasons)
public class Fraction : IEquality<Fraction>
{
public override int GetHashCode()
{
// ...
}
}
2. It verifies that the method Object.Equals(object) behaves as expected. The method should return true when two instances of the same equivalence class are compared together, or when an instance is compared to itself. For nullable types (reference types, or value types nested in a Nullable class), it verifies you dealt correctly with null references. The method should return False when called against a null reference.
public class Fraction : IEquality<Fraction>
{
public override bool Equals(object obj)
{
// ...
}
}
3. Same verifications as point 2; but for the strongly typed version of the Equals method (which is actually the only method that the IEquatable<T> interface requires you to implement).
public class Fraction : IEquality<Fraction>
{
public bool Equals(Fraction other)
{
// ...
}
}
Whenever implementing IEquatable<T>, it is usually recommended to overload the two equality operators as well. By default, the equality contract verifier will evaluate them too. If your class does not override those operators, consider disabling that feature by setting to false the named parameter ImplementsOperatorOverload.
[TestFixture]
[VerifyEqualityContract(typeof(Fraction), ImplementsOperatorOverload = false)]
public class FractionTest : IEquivalenceClassProvider<Fraction>
{
// ...
}
4. It verifies that the equality operator (==) behaves as expected. The verifications are the same as for points 2 and 3. For nullable types, it is also verified that two null references are considered to be equal.
public class Fraction : IEquality<Fraction>
{
public static bool operator ==(Fraction left, Fraction right)
{
// ...
}
}
5. It verifies that the inequality operator (!=) behaves as expected.
public class Fraction : IEquality<Fraction>
{
public static bool operator !=(Fraction left, Fraction right)
{
// ...
}
}
2008/08/25
Contract Verifiers - Part II. - The Exception Contract Verifier
- Part I. - Introduction
- Part II. - Exception Contract Verifier
- Part III. - Equality Contract Verifier
- Part IV. - Comparison Contract Verifier
Designing custom exceptions is not a so easy task. The recommended guidelines are relatively strict; and writing unit tests for them is fastidious. That is why the exception contract verifier is useful. It helps testing your custom exceptions, and catching potential design and behavioral problems.
The contract verifier is available as a companion attribute for your test fixture. It adds the necessary test methods, which verify that your exception is well designed and behaves as expected.
The contract verifier is available as a companion attribute for your test fixture. It adds the necessary test methods, which verify that your exception is well designed and behaves as expected.
public class MyCustomException : Exception
{
// ...
}
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException))]
public class MyCustomExceptionTest
{
}
Although the test fixture contains no explicit test methods, the test runner will execute 5 tests provided by the contract verifier attribute. Each of them corresponds to a specific guideline:
1. Provides a public default parameter-less constructor.
2. Provides a public constructor taking one single parameter for the error message .
3. Provides a public constructor taking two parameters for the error message and an inner exception.
public class MyCustomException : Exception
{
public MyCustomException()
{
// ...
}
public MyCustomException(string message)
{
// ...
}
public MyCustomException(string message, Exception innerException)
{
// ...
}
}
If for some reason, you decide that your custom exception should handle with construction differently, you can disable the 3 tests above by setting to false the named parameter ImplementsStandardConstructors.
[TestFixture]4. Decorates your custom exception with the attribute System.SerializableAttribute.
[VerifyExceptionContract(typeof(MyCustomException), ImplementsStandardConstructors = false)]
public class MyCustomExceptionTest
{
}
[Serializable]5. Implements a protected constructor for data serialization.
public class MyCustomException : Exception
{
}
public class MyCustomException : Exception, ISerializable
{
protected MyCustomException(SerializationInfo info, StreamingContext context)
{
// ...
}
}
Again, in some scenarios, you might decide to not support the serialization process (if your code base targets the compact framework, for instance). Disable the serialization tests by setting to false the named parameter ImplementsSerialization.
[TestFixture]
[VerifyExceptionContract(typeof(MyCustomException), ImplementsSerialization = false)]
public class MyCustomExceptionTest
{
}
When the serialization tests are enabled, the test runner will verify the preservation of data during a complete round trip serialization operation. This will be done for all the constructors; including the 3 recommended standard constructors described above.
The following sample shows the complete code for a general-purpose custom exception; and the test fixture verifying it.
The following sample shows the complete code for a general-purpose custom exception; and the test fixture verifying it.
[Serializable]
public class MyCustomException : Exception, ISerializable
{
public MyCustomException()
{
}
public MyCustomException(string message)
: base(message)
{
}
public MyCustomException(string message, Exception innerException)
: base(message, innerException)
{
}
protected MyCustomException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
}
[TestFixture]And here is the report generated by Gallio for that test fixture:
[VerifyExceptionContract(typeof(MyCustomException))]
public class MyCustomExceptionTest
{
}
2008/08/19
Contract Verifiers - Part I. - Introduction
- Part I. - Introduction
- Part II. - Exception Contract Verifier
- Part III. - Equality Contract Verifier
- Part IV. - Comparison Contract Verifier
Writing unit tests is costly. Surely less costly than not writing them, but costly too. A commonly accepted trade-off consists in not testing trivial features, or worst, repeating features. You know, all those dozens of little classes that implement repeatedly a roughly similar stuff: custom exceptions, event arguments, equatable, comparable, or disposable interfaces, etc. If you write unit tests, you know how painful and uninteresting it is to test those objects.
Honestly, look at the last project you have worked on. Do you see all those custom exceptions you have written? Have you really tested their serialization and unserialization support? Look at these classes implementing the generic IEquatable interface. Have you verified that the equality behaves correctly in all the possible ways (to a null reference, to itself, commutability of the operation, etc?) If the answer is no, then I sympathize. If yes, well, it was a so boring job, wasn't it?
The next release of Gallio (alpha 4) will come with a cool feature called Contract Verifiers. Actually, it is already available in the alpha 3, but under a preliminary form which will be probably marked as obsolete in the future (or perhaps simply removed).
Honestly, look at the last project you have worked on. Do you see all those custom exceptions you have written? Have you really tested their serialization and unserialization support? Look at these classes implementing the generic IEquatable interface. Have you verified that the equality behaves correctly in all the possible ways (to a null reference, to itself, commutability of the operation, etc?) If the answer is no, then I sympathize. If yes, well, it was a so boring job, wasn't it?
The next release of Gallio (alpha 4) will come with a cool feature called Contract Verifiers. Actually, it is already available in the alpha 3, but under a preliminary form which will be probably marked as obsolete in the future (or perhaps simply removed).
[Update; Actually, Jeff has just removed it from the repository.]
The principle of the contract verifiers is simple. Basically, a contract verifier is a set of predetermined test methods that you can attach to a given test fixture without actually writing them (we could describe it as a kind of Mixin). The test methods will verify for you that the object fulfills the given implementation contract. It is an all-in-one functionality because the test methods have already been written for you. They also support some configuration; so they can be used in different scenarios.
Let's take an example. Imagine you need to write a class A implementing a well-known interface, or deriving from a well-known base class. Let's call it X. You will have to override the necessary members of X, so that A works correctly; and to write a bunch of unit tests that will verify that the functionalities of X (let's call them x1, x2 and x3) work as expected in your implementation of A. Fortunately, Gallio comes with a contract verifier for X. We will name it XContractVerifier. Unit testing A comes suddenly easy:
[TestFixture]
[TestsOn(typeof(A)]
[XContractVerifier(option1=blabla, option2=blabla)]
public class ATest
{
// My other own tests here...
}
Thanks to the XContractVerifier attribute, and during the execution of the test fixture, the test runner of Gallio will add behind the scene, the necessary test methods that will verify that A implements correctly x1, x2, and x3.
Gallio Alpha 4 includes three built-in contract verifiers (it is a beginning):
Gallio Alpha 4 includes three built-in contract verifiers (it is a beginning):
It is interesting to notice that the internal working of the contract verifiers is based on the remarkable capability of the Gallio exploration engine to dynamically attach child test methods to a given fixture; although those test methods do not physically exist.
2008/07/25
Contributing to Gallio
It is not the first time I have a close look at the source code of an open source project. My hard drive has a dedicated folder where I can find the internals of NHibernate, Paint.NET, Rhino, or Castle. Studying and analyzing good code, and understanding the whys and hows of its internal design, is worth the time you will invest. There is so much to learn from the guru coders who lead those projects.
However, it is the first time I have the desire to contribute significantly to such a project (Other than submitting small patches which fix minor issues). I discovered the Gallio Automation Platform early in its alpha 1 stage. It was an exciting project with tons of innovative concepts; and I have eagerly followed the development phases since that very moment.
When Jeff Brown, the lead developer of Gallio, mentioned in his blog the existence of the contract verifiers, I immediately thought that it was a cool feature that I would like to work on. Two months later and with Jeff's invaluable help, I am in the process of refactoring completely the implementation of the contract verifiers for MbUnit v3, based on Jeff's own road map and ideas. I am more than happy to work on such a great project during my free time.
Expect to learn about contract verifiers later on this blog.
However, it is the first time I have the desire to contribute significantly to such a project (Other than submitting small patches which fix minor issues). I discovered the Gallio Automation Platform early in its alpha 1 stage. It was an exciting project with tons of innovative concepts; and I have eagerly followed the development phases since that very moment.
When Jeff Brown, the lead developer of Gallio, mentioned in his blog the existence of the contract verifiers, I immediately thought that it was a cool feature that I would like to work on. Two months later and with Jeff's invaluable help, I am in the process of refactoring completely the implementation of the contract verifiers for MbUnit v3, based on Jeff's own road map and ideas. I am more than happy to work on such a great project during my free time.
Expect to learn about contract verifiers later on this blog.
2008/07/05
Every interface is public
A few days ago, I posted a comment on timm's blog. I made a remark about the difficulties that most .NET developers had and still have when they have to handle with Stream objects to read and write text files; especially in the early days of the .NET framework, when methods such as File.ReadAllText and File.ReadAllLines did not exist yet.
Then, I remembered reading that on the book written by Brad Adams and Krzysztof Cwalina: "Framework Design Guidelines".
Although published already two years ago, this is still an excellent reference about naming conventions, sane type design and class library extensibility. Perhaps it would deserve a second edition with additional stuff about C#3, Linq, WPF, WCF, etc. This book is also a source of valuable comments and amusing little stories from various actors in the .NET framework world, explaining why some classes were designed the way they are, or which were the difficulties they met while designing the class library. Delightful…
However, there is a little point in that book which I tend to disagree with. It is apparently only a detail, but which hides perhaps an entirely different philosophical point of view about code design. The authors say about naming guidelines:
Although adopting these naming conventions as general code development guidelines would result in more consistent naming throughout your code, you are required only to apply them to APIs that are publicly exposed.
As a corporate developer, I write code that has little chance to be read or to be reused by other developers outside my work place. The question I ask myself, is what exactly "publicly exposed APIs" mean? Technically speaking, it should be any type or member with a public scope. But actually, when my colleague reads my code, he has also access to objects with private or internal scope. When I download the code of an open-source project such as Rhino or the promising Gallio, I also have access to internal types. In VS2008, we have also the possibility to see the source code of the class library, which has not a public scope. And finally, when I stop working for a while on a project of mine, the person I am when I resume the job is not exactly the same. I have perhaps forgotten how the system was designed; so I come with a new fresh eye and new recently acquired experience.
I believe that "publicly exposed APIs" is not synonym of "types and members with a public scope". In fact, I tend to consider every single line of code as public at a certain level: public to other anonymous developers, public to your colleagues, or only public to you. As soon as a line of code is written, it can obviously be read as well. Maybe not by everyone, but at least by you and the other developers who work with you.
That's why I think it does not make sense to apply partially such a guideline. It's like pushing dust under the carpet. For sure, people passing next to the house will never know you do that. They will only see the "public interface", that is the front of your home. But your family might discover it, or your neighbor, who will visit you one day or another. But more important, YOU will know it. If you can live with that mess in your code, I have the bad feeling that you are not rigorous enough to become a good developer.
2008/06/24
Better be deaf
As promised, today, I am going to share with you an amusing anecdote which demonstrates how a poorly designed user interface can ruin a good and simple system into something which is hardly usable.
I already talked you about my elevator front panel. I know another device in my apartment building which deserves, I believe, some words: my entry phone!
In fact, my entry phone has nothing special compared to the other entry phones in the building. They are all the same. Its usage is also very usual. Each phone is connected to a microphone located at the entrance of the building. When a visitor wants to enter the building, she presses a button close to the door. The host hears a short tone melody which warns about the presence of a visitor. The host comes to the entry phone, and speaks into the microphone to the visitor. Eventually, the host lets the visitor enter the building by pressing another button. As I said, nothing unusual.
But the entry phone has a second functionality. There is also a ring at the door of the apartment. That ring is connected to entry phone as well. When a visitor rings, the host hears a melody which warns about the presence of the visitor (deja vu?). The host comes at the door, looks into the peephole, and eventually opens the door and welcomes the visitor.
Perhaps you already figured out what is the problem: the tone melodies. In both the scenarios, the entry phone plays a tone melody and there is no easy way to know where the visitor is: at the entrance of the building, or at the entrance of the apartment. One could argue that any visitor will first come at the entrance of the building, but it is no help for several reasons: the entrance of the building was sometimes left unclosed by a previous visitor; or you are sometimes visited by your own neighbor; or some people are actually visiting all the inhabitants so they need obviously to enter the building only once.
At the beginning, I did believe it was only me. Perhaps I have a bad sound memory, or perhaps I would finally learn for what event is each melody. But surprisingly, ten years after, the problem remains the same. Each time the phone entry plays the little music, I don't know where to go: at the entry phone or at the door. My wife does experience the exact same problem. But it seems my neighbors too! A few weeks ago, I needed to talk to one of them. So I rang at his door. After some seconds, I clearly hear my neighbor not too far behind the door trying to vainly speak in the entry phone: "Hello? Hello? Who is it?" So I just knock at the door to tell him he should stop speaking alone in the microphone and come at the door instead.
The fact is that the system exposes two different features through a problematic user interface:
I already talked you about my elevator front panel. I know another device in my apartment building which deserves, I believe, some words: my entry phone!
In fact, my entry phone has nothing special compared to the other entry phones in the building. They are all the same. Its usage is also very usual. Each phone is connected to a microphone located at the entrance of the building. When a visitor wants to enter the building, she presses a button close to the door. The host hears a short tone melody which warns about the presence of a visitor. The host comes to the entry phone, and speaks into the microphone to the visitor. Eventually, the host lets the visitor enter the building by pressing another button. As I said, nothing unusual.
But the entry phone has a second functionality. There is also a ring at the door of the apartment. That ring is connected to entry phone as well. When a visitor rings, the host hears a melody which warns about the presence of the visitor (deja vu?). The host comes at the door, looks into the peephole, and eventually opens the door and welcomes the visitor.
Perhaps you already figured out what is the problem: the tone melodies. In both the scenarios, the entry phone plays a tone melody and there is no easy way to know where the visitor is: at the entrance of the building, or at the entrance of the apartment. One could argue that any visitor will first come at the entrance of the building, but it is no help for several reasons: the entrance of the building was sometimes left unclosed by a previous visitor; or you are sometimes visited by your own neighbor; or some people are actually visiting all the inhabitants so they need obviously to enter the building only once.
At the beginning, I did believe it was only me. Perhaps I have a bad sound memory, or perhaps I would finally learn for what event is each melody. But surprisingly, ten years after, the problem remains the same. Each time the phone entry plays the little music, I don't know where to go: at the entry phone or at the door. My wife does experience the exact same problem. But it seems my neighbors too! A few weeks ago, I needed to talk to one of them. So I rang at his door. After some seconds, I clearly hear my neighbor not too far behind the door trying to vainly speak in the entry phone: "Hello? Hello? Who is it?" So I just knock at the door to tell him he should stop speaking alone in the microphone and come at the door instead.
The fact is that the system exposes two different features through a problematic user interface:
- It uses a single communication channel: sound. No light, no display, nothing else. But it's not the worst issue, as most of the existing user interfaces do the same without any major problem (phones, traffic signs, and even software applications which most of the time, focus on visual user experience only.)
- Both the features are exposed through symbols (the melodies) which are totally disconnected from their meaning. They do not evoke anything. They are just a short abstract series of unrelated tones without any semantic. You have to be very imaginative to be able to associate them with one feature or another.
I am unsure what solution could have been chosen to design an effective interface for that system: visual support, multiple sound source (at the door and at the entry phone), better distinct melodies, artificial voice? (I'm a bit reluctant about using artificial human voice when not absolutely necessary. It tends to be quickly irritating. This is something I will probably come back later in a future post.)
What I know for sure is that the designer should have issued a meeting notice for some brainstorm with his colleagues.
What I know for sure is that the designer should have issued a meeting notice for some brainstorm with his colleagues.
2008/06/16
Oversimplification
The lead developer of NDepend, Patrick Smacchia, explains what he considers a key to a successful interface design:
Make the simple things simple and hard things possible. IMHO, this tenet applies perfectly in how UI should be designed. Typically, the most direct way to use a UI control should result in the most awaited feature from a user perspective (make the simple things simple). Then, some extra/hidden UI control facilities can be added to the control to support more in-depth scenario (make hard things possible).
But achieving simplicity in interface design is everything but an easy task. A common mistake (but apparently not so commonly known) is the oversimplification. It consists in an unfortunate too low threshold in what the designer considers simple things and hard things. It's an error because it may lead the average user to frustration. Every time she needs to activate such a designated "hard" feature, although she would consider it as a basic feature, she has to manipulate a more complex interface to access to the hidden functionality.
A good example is the automatic collapsed/expanded menu of Microsoft Office 2003. Why features like text replace or page setup were put by default in the "hard things" list? The authors have apparently decided that those functionnalities are less commonly used. Perhaps it is true actually, for most users. But what about people who, for a reason or another, needs to use them twenty or fourty times a day?
A good example is the automatic collapsed/expanded menu of Microsoft Office 2003. Why features like text replace or page setup were put by default in the "hard things" list? The authors have apparently decided that those functionnalities are less commonly used. Perhaps it is true actually, for most users. But what about people who, for a reason or another, needs to use them twenty or fourty times a day?
But generally speaking, determining what is simple and what is hard is a difficult task; especially if you target a wide audience from the modest grandma to the tech geek. Mitigation is always possible by providing some options for personal customization, or automatic adaptation based on the most frequently used commands (like the menus described aboved, in fact). But in the real world (I mean the non-IT world), this is sometimes just impossible. Look at this remote control device I saw recently in a paper advertisment.
Although very simple, the idea is great and not so common for that kind of device. When folded, it presents just a few buttons for the very basic features: turn the TV on and off, change the channels, etc. Only when unfolding it, you can access to the advanced functionalities. One can suppose that a feature like video recording or TV internet access is probably only accessible from inside the remote control. This is surely not an issue for grandma. But for the tech geek, after all, it’s perhaps not a so well designed device.
Although very simple, the idea is great and not so common for that kind of device. When folded, it presents just a few buttons for the very basic features: turn the TV on and off, change the channels, etc. Only when unfolding it, you can access to the advanced functionalities. One can suppose that a feature like video recording or TV internet access is probably only accessible from inside the remote control. This is surely not an issue for grandma. But for the tech geek, after all, it’s perhaps not a so well designed device.
Think twice about your audience and its level of expertise before implementing such a solution, whatever your system is software- or hardware-based.
2008/06/09
Turn right... but not so much!
In his excellent blog, Jeff Atwood already talked several times about road signs in the perspective of user interface design. Indeed, as a professional software developer, I have occasionally to design interfaces (end-user panels, API, or frameworks); and I share the fascination of Jeff for road signs and for symbols usage in general.
In a previous article, I already made an analogy between road signs and software interfaces, about the necessity to design consistent models.
But even though you follow your own design rules and make a consistent interface, you still have make a proper usage of it. Let's continue our analogy with traffic signs and look at the following picture I took recently there.
This example is a perfect illustration of an interface with a good design (simple sign intuitively understandable) but used improperly or inconsistently. Or did I miss something ?
But even though you follow your own design rules and make a consistent interface, you still have make a proper usage of it. Let's continue our analogy with traffic signs and look at the following picture I took recently there.
So what? Obviously, you must turn right soon. But why installing two nearly identical traffic signs located at 10 meters from each other. The first sign indicates that the road turns 90° right; but a few meters later, it's only 45°.
According to the local driving rules (Page 121), the two signs have actually the same meaning: they indicate to the drivers the obligatory way(s) to follow according to the direction indicated by the arrow. So in fact, I cannot see any logical nor legal reason for the presence of those signs with a different arrow.
According to the local driving rules (Page 121), the two signs have actually the same meaning: they indicate to the drivers the obligatory way(s) to follow according to the direction indicated by the arrow. So in fact, I cannot see any logical nor legal reason for the presence of those signs with a different arrow.
This example is a perfect illustration of an interface with a good design (simple sign intuitively understandable) but used improperly or inconsistently. Or did I miss something ?
2008/06/01
Written interfaces and localization
I live in a non-English speaking country. Although a significant part of the population can speak English a little, the vast majority of it does not know a single word. This is the case for my wife. She perfectly speaks Portuguese and French, and she has some solid notions of Italian and Spanish, but no English at all.
Of course, there is nothing wrong. The most annoying for her is the plethora of little devices we daily uses (computers, recorders, music players, etc.) and which have been designed for international marketing but without any consideration for localization of the user interface. Look at that radio alarm for instance:
Of course, there is nothing wrong. The most annoying for her is the plethora of little devices we daily uses (computers, recorders, music players, etc.) and which have been designed for international marketing but without any consideration for localization of the user interface. Look at that radio alarm for instance:
Each time my wife wants to change the settings, she has to ask me whether she must switch it on or off. That is because she has no idea what "on" or "off" means. For her, it could be so well written "sglurmf" and "zxqwaghs"! Without any reference, it is very difficult to remember a foreign word and its meaning.
In the IT world and especially on the Internet, the possibility to switch between languages is so common that we do not notice it any more. But unfortunately, it is not so easy for non-software user interfaces.
2008/05/26
Unfriendly traffic signs
In Europe, like in North America, and like in many other regions in the world, everything seems to be have been built around the concept of car transportation: from the location of the buildings and how they are designed, to the daily social organization of everyone. This has an unexpected but important consequence which is directly in relation with user interfaces.
As a driver, passenger, cyclist or simple pedestrian; in a large city, or in the countryside; we are literally surrounded by traffic signs. Everywhere. From the largest highway, to the most modest track suitable for vehicles. It might be surprising, but traffic signs are actually user interfaces. They represent an interface between the travelers and local authorities, by communicating about potential risks, limitations, and other pieces of information. But like any other kind of user interfaces, traffic signs need to be well designed.
In the driving school, I learnt a simple rule which helps understanding most of the traffic signs found in several countries of Western Europe. Consider the shape and the dominant colors of any sign.
Great, isn't it? It seems so easy. Most probably, a similar set of rules exists in your own region. Next time you travel in car with children, you can propose to them a funny game. Explain the rules above, then let them imagine what is the true meaning of each traffic sign they will see. Thanks to the shape, the color and the symbols inside the sign (most of them are relatively pertinent); and with some little help, they should quickly find intuitively the correct answer.
Unfortunately (and this is actually the real point of my post), you will also very quickly meet some signs which do not fit into any of the categories described previously. It's not a disc, nor a square; unexpected color, obscure symbol. No chance to "guess" the real semantic, if you don't know it already.
Worse, those four signs represent about 10% of all the signs installed all along the roads. My question is the following. Why having created a very nice set of rules which apply only to 90% of the traffic signs, and let all the remaining elements with a complete customized and chaotic style? Does it make really any sense? In fact, I have no answer concerning that specific example; but there is a moral to this story. It is applicable to software engineering, but probably to any other engineering discipline as well.
As a designer, you have to establish rules and principles which will describe the general working and the usage of your system. But you have also to apply those rules from start to finish. Do not suffer any exception. It is commonly accepted that exception proves the rule. But I think it's only an easy excuse to not refactor your work when it needs to (Agile followers will appreciate the generalization of the concept). If things do finally not match well to the rules, it is usually a smell of poor preliminary specifications. Change the things or rewrite the rules, but make easier the life of your future users. As a designer, if you don't follow your own rules, who else?
As a driver, passenger, cyclist or simple pedestrian; in a large city, or in the countryside; we are literally surrounded by traffic signs. Everywhere. From the largest highway, to the most modest track suitable for vehicles. It might be surprising, but traffic signs are actually user interfaces. They represent an interface between the travelers and local authorities, by communicating about potential risks, limitations, and other pieces of information. But like any other kind of user interfaces, traffic signs need to be well designed.
In the driving school, I learnt a simple rule which helps understanding most of the traffic signs found in several countries of Western Europe. Consider the shape and the dominant colors of any sign.
- A white disc surrounded by a red border forbids.
- A white triangle surrounded by a red border warns about a danger.
- A blue square informs.
- A blue disc constrains.
Great, isn't it? It seems so easy. Most probably, a similar set of rules exists in your own region. Next time you travel in car with children, you can propose to them a funny game. Explain the rules above, then let them imagine what is the true meaning of each traffic sign they will see. Thanks to the shape, the color and the symbols inside the sign (most of them are relatively pertinent); and with some little help, they should quickly find intuitively the correct answer.
Unfortunately (and this is actually the real point of my post), you will also very quickly meet some signs which do not fit into any of the categories described previously. It's not a disc, nor a square; unexpected color, obscure symbol. No chance to "guess" the real semantic, if you don't know it already.
Worse, those four signs represent about 10% of all the signs installed all along the roads. My question is the following. Why having created a very nice set of rules which apply only to 90% of the traffic signs, and let all the remaining elements with a complete customized and chaotic style? Does it make really any sense? In fact, I have no answer concerning that specific example; but there is a moral to this story. It is applicable to software engineering, but probably to any other engineering discipline as well.
As a designer, you have to establish rules and principles which will describe the general working and the usage of your system. But you have also to apply those rules from start to finish. Do not suffer any exception. It is commonly accepted that exception proves the rule. But I think it's only an easy excuse to not refactor your work when it needs to (Agile followers will appreciate the generalization of the concept). If things do finally not match well to the rules, it is usually a smell of poor preliminary specifications. Change the things or rewrite the rules, but make easier the life of your future users. As a designer, if you don't follow your own rules, who else?
2008/05/20
Easy symbols
Symbols are very important in user interfaces. Due to space constraints, the designer often needs to represent concepts with symbols. They usually act as visual or auditive shortcuts (I have a funny anecdote about an exclusively auditive interface, that I will soon talk about in a future post.)
Recently, I had to work on the conception and the development of a large software project with a very significant user interface containg a lot of forms and panels. In order to improve the usability of the system, I decided that I would need a nice but simple icon library to decorate the interface.
Being not a graphic designer, I have found a simple way to quickly create such an icon library.
1. Get a basic and sober icon frame. You can find plenty of them for free on Internet.
2. Open the frame image in your favorite image processor.
3. Add a new layer above the frame, then write a character with any symbolic font such as Webdings or Wingdings.
4. Add a shadow or some other effect. Et voilĂ ! In less than 10 minutes, you get a nice set of icons you can extend and vary ad infinitum.
Recently, I had to work on the conception and the development of a large software project with a very significant user interface containg a lot of forms and panels. In order to improve the usability of the system, I decided that I would need a nice but simple icon library to decorate the interface.
Being not a graphic designer, I have found a simple way to quickly create such an icon library.
1. Get a basic and sober icon frame. You can find plenty of them for free on Internet.
2. Open the frame image in your favorite image processor.
3. Add a new layer above the frame, then write a character with any symbolic font such as Webdings or Wingdings.
4. Add a shadow or some other effect. Et voilĂ ! In less than 10 minutes, you get a nice set of icons you can extend and vary ad infinitum.
2008/05/16
The elevator front panel
I live with my lovely wife Jacinta in a nice duplex located at the third floor of an apartment block. We have bought it some years ago. The building has 4 floors. But surprisingly, when entering the elevator, you are faced with the following panel :
One could say it is just funny. But my opinion is that having a complete 10-digits pad and a 3-digits display, to only just move between floor 0 and 3, is plain stupid. Realize that 70% of the panel is completely useless !
If you look carefully, you can even see a dot key! Frankly, I have no idea what usage such a key may have in an elevator. Perhaps someone could desire to go to the floor 2.5 ? Earlier today, I made several trials. And fortunately, the elevator had silently rejected my request to visit hell when I typed -666.
More seriously, the reason why the architect took the decision to install such a panel, is far beyond my understanding. But there is a lesson to remember.
When designing a system, you do not have always the possibility to create yourself a dedicated user interface. Sometimes, you have no other choice but to apply an existing template. Should this happen, do not follow the famous proverb which says "He who can do more can do less". In term of user interface, this will lead you inevitably to a failure. As Patrick Smacchia explained recently in his excellent blog about NDepend, let "make the simple things simple (...)". Interface designers should always remember that very sentence.
When designing a system, you do not have always the possibility to create yourself a dedicated user interface. Sometimes, you have no other choice but to apply an existing template. Should this happen, do not follow the famous proverb which says "He who can do more can do less". In term of user interface, this will lead you inevitably to a failure. As Patrick Smacchia explained recently in his excellent blog about NDepend, let "make the simple things simple (...)". Interface designers should always remember that very sentence.
2008/05/15
A few preliminary words
As the title of this blog might suggest, the main topic of my future posts will be user interfaces. But despite the fact that I work as a corporate software engineer in a large automotive company, I do not intend to restrict myself to software interfaces. In fact, I would like to speak with you about all the kind of human/machine interfaces, which we, as human beings living in a modern society, have to deal with daily... more or less successfully.
I am going to expose my thoughts about what make interfaces good or bad. I will relate anecdotes inspired from my professional experience, but also from my private life concerning all those funny, stupid, or amazing interfaces which allow (or sometimes prevent) us to interact with the rest of the world. We will also try to have a look at what and who is hidden behind interfaces.
I hope you will enjoy that blog as much as I will enjoy writing it.
See you soon...
I am going to expose my thoughts about what make interfaces good or bad. I will relate anecdotes inspired from my professional experience, but also from my private life concerning all those funny, stupid, or amazing interfaces which allow (or sometimes prevent) us to interact with the rest of the world. We will also try to have a look at what and who is hidden behind interfaces.
I hope you will enjoy that blog as much as I will enjoy writing it.
See you soon...
Subscribe to:
Posts (Atom)