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.
What is 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]
On the process of implementing custom StyleCop rules
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:
- Implement a new style rule
- Compile the custom rule assembly
- Copy the assembly to StyleCop’s folder
- Open a new instance of Visual Studio
- Write code to test the new rule
- Run the new rule
- More often than not – find a bug. close visual studio and go to step #1
Instead I got the following:
- Write failing test
- Run test
- Implement a new style rule
- Run the test again
- If test still fail go to step #1
Better, Simpler, Faster
Writing unit tests for my custom rules
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:
- Lines 3–6: Creation of StyleCop’s types I needed to parse the code file.
- Line 8: Due to some design fluke I needed a CodeDocument to pass to the parser. Unfortunately CodeDocument is an abstract class so I just inherited it in a dummy class I’ve created. No need to implement anything because this instance will be replaced after the new command.
- Line 10: parse the source file – and that’s it.
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:
- Line 2: I’ve created a new project that contains my test classes. I’ve used MSTest Deploy to make sure the file I want will be copies to the place the tests are run.
- Lines 5–6: I don’t really need to explain that one – right?
- Line 8: invoking the helper method
- Line 10: Create a new instance of the class that holds the custom rules
- Line 12: Using Isolator to make sure that AddViolation does not get called. In order to call it I would have needed to initialize a lot more of StyleCop’s environment and instead I’ve used this simple line
- Line 14: Call my method under test
- Lines 16-19: Using Isolator to test that AddViolation was called and that the correct string was passes.
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.
Can you post the code for DummyCodeDocument? I followed this and was unable to get my code to run correctly.
Can you post the code for DummyCodeDocument
It's been a while and I couldn't find it, the company where I wrote this code went bankrupt.
However the DummyCodeDocument is just an empty class implementing CodeDocument:
“CodeDocument is an abstract class so I just inherited it in a dummy class I’ve created. No need to implement anything because this instance will be replaced after the new command.”
what namespace you used to access “Isolate.WhenCalled” I am getting error “The name 'Isolate' does not exist in the current context” in function “AnalyzeDocument_FileNameDoesNotMustMatchClassNameRule_AddViolationCalledWithCorrectRule”
That's Typemock Isolator – a .NET mocking framework I've been using