RSS
Facebook
Twitter

Saturday, July 03, 2010

Unit testing custom StyleCop rules using Typemock Isolator

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:

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:




  1. Implement a new style rule


  2. Compile the custom rule assembly


  3. Copy the assembly to StyleCop’s folder


  4. Open a new instance of Visual Studio


  5. Write code to test the new rule


  6. Run the new rule


  7. More often than not – find a bug. close visual studio and go to step #1



Instead I got the following:




  1. Write failing test


  2. Run test


  3. Implement a new style rule


  4. Run the test again


  5. 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.



1 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...