I’ve never was a big fan of “coding standard” – Although I always thought that the same style should be kept throughout a project or even the entire company’s code base - the idea of forcing developers to write the same code based on a document nobody ever read seemed just wrong.
Fast forward a few years and suddenly I’m responsible that my team’s code will be written according to the coding standard of the company.
At first I thought it shouldn’t be too hard – everybody knows the coding standard – and boy was I wrong. The coding standard document was copied from a previous document and even the developers that did read it couldn’t remember all of it’s 20+ pages of rules and ideas.
It was clear that I needed help – preferably in a form of some tool that would process my team’s code. After looking a bit I’ve found that tool – named StyleCop.
StyleCop is a free static code analysis tool from Microsoft that checks C# code for conformance to StyleCop's recommended coding styles and a subset of Microsoft's .NET Framework Design Guidelines. StyleCop analyzes the source code, allowing it to enforce a different set of rules from FxCop. The rules are classified into the following categories:
- Documentation
- Layout
- Maintainability
- Naming
- Ordering
- Readability
- Spacing
StyleCop includes both GUI and command line versions of the tool. It is possible to create new rules to be used.
StyleCop was re-released as an open source project in April of 2010, at http://stylecop.codeplex.com.
[From Wikipedia]
Back to the problem at hand – although my company has a coding standard it’s not exactly similar to Microsoft’s so I needed to develop some custom rules luckily this topic was already covered by my fellow bloggers:
Creating Custom StyleCop Rules in C# (Scott White)
Creating Custom Rules for Microsoft Source Analyzer (Paul Jackson)
A custom Rule would look something like:
[SourceAnalyzer(typeof(CsParser))]
public class NamingRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
var csdocument = (CsDocument)document;
if (csdocument.RootElement != null && !csdocument.RootElement.Generated)
{
csdocument.WalkDocument(VisitElement, null, null);
}
}
private bool VisitElement(CsElement element, CsElement parentElement, object context)
{
if (element.Generated)
{
return true;
}
if(element.ElementType == ElementType.Class && !(element.Parent is Class))
{
var csClass = (Class)element;
var fileName = csClass.Document.SourceCode.Name;
var fileNameWithoutExtension = string.Format("class {0}", Path.GetFileNameWithoutExtension(fileName));
var className = csClass.GetShortName();
if(fileNameWithoutExtension.Equals(className) == false)
{
AddViolation(element, element.LineNumber, "FileNameMustMatchClassName");
}
}
return true;
}
}
In case you were wondering the code above checks that a class resides in a file with the same name.
But there is a problem with writing style rules – they look trivial at first but tend to accumulate corer cases as you progress. My solution was to find a way to test the custom rules I’ve written so that I won’t accidently break during my work.
The added value of using unit tests is that I didn’t need to manually test my new rules – a process that consists from the following steps:
Instead I got the following:
Better, Simpler, Faster
I’ve wanted to be able to parse an actual file and analyze it using StyleCop and my new rules – using some Reflector magic I was able to discover how StyleCop worked and I was able to write the following “helper” method:
1: public static CodeDocument ParseDocument(string codeFileName, string projectFileName)
2: {
3: var parser = new CsParser();
4: var configuration = new Configuration(null);
5: var project = new CodeProject(projectFileName.GetHashCode(), projectFileName, configuration);
6: var sourceCode = new CodeFile(codeFileName, project, parser);
7:
8: CodeDocument document = new DummyCodeDocument(sourceCode);
9:
10: parser.ParseFile(sourceCode, 0, ref document);
11:
12: return document;
13: }
The method receives a file name and a project name and creates StyleCop’s representation of that file.
Details:
Armed with a method that enable me to parse code files I was now able to test my new rule – almost, I’ve needed to fake a call to AddViolation and verify it got called and for that I’ve used Typemock Isolator:
1: [TestMethod]
2: [DeploymentItem(@"..\..\..\.StyleCop.Rules.Tests.TestClasses\FileNameMustMatchClassNameRule.cs")]
3: public void AnalyzeDocument_FileNameDoesNotMustMatchClassNameRule_AddViolationCalledWithCorrectRule()
4: {
5: const string projectFileName = @"StyleCop.Rules.Tests.TestClasses.csproj";
6: const string codeFileName = @"FileNameMustMatchClassNameRule.cs";
7:
8: var document = TestHelpers.ParseDocument(codeFileName, projectFileName);
9:
10: var namingRules = new NamingRules();
11:
12: Isolate.WhenCalled(() => namingRules.AddViolation(null, 0, string.Empty)).IgnoreCall();
13:
14: namingRules.AnalyzeDocument(document);
15:
16: Isolate.Verify.WasCalledWithArguments(() => namingRules.AddViolation(null, 0, string.Empty))
17: .Matching(objects => {
18: var ruleName = objects[2].ToString();
19: return ruleName.Equals("FileNameMustMatchClassNameRule");
20: });
21: }
Details:
That’s enough code for now. Using this method I was able to write tests to all of the custom rules I’ve implemented.
As for my opinion about the need for an official coding style - it changed, after fixing a few (thousands) lines – the code actually look better and more importantly it’s more readable.
Labels: .NET, C#, Mock objects, MSTest, Typemock, Unit tests