CodeStore - Part 3 of n (Cecil, Moq)

This is the third part of the series on building CodeStore. In this blog post, I'll finish off the first user story which was

1)  As a customer, I want to query the Assembly data in the database so that I can get metrics about the assembly.

 

The story is further decomposed into two tasks:

 

a)   Read Assembly Data

b)   Store Assembly Data in SQLite using Nhibernate.

 

We start with completing the unit test for AssemblyDataRepository. If you remember in Part II  that I changed the Nhibernate integration to use Castle Nhibernate repository and therefore passing in the ISessionManager to the repository classes.

 

So, to test AssemblyDataRepository I'm going to mock ISessionManager to test the behaviour of the method.  As this project is about learning so I've decided to use Moq (http://code.google.com/p/moq/) as the mocking container. 

 

Moq uses lambdas to set expectations which takes some practice to get used to. You can read more about Moq on Daniel Cazzulino blog (http://www.clariusconsulting.net/blogs/kzu/).

 

So we start with a test for adding assembly data using the repository.

 

 

It uses the same test flow as other testing (mocking) frameworks.

  1. Setup data
  2. Setup mocks, expectations
  3. Run SUT
  4. Verity the expectations

Once the test is passed, we can check off the second task of the story. I can write few more tests but let's not worry about that for now. The next thing to look into is actually parsing the assembly and extracting the required information from assembly metadata. To read IL, I'm going to use Cecil which can be downloaded from http://www.mono-project.com/Cecil.

 

The initial model for code analysis engine looks like this. (Please note that the following diagram is the snapshot taken at the end of this blog post)

 

I started with CodeAnalysisEngine whose job is to ask the CodeAssemblyLoader to load the assembly and then pass it to CodeAssemblyParser to do the actual information extraction.

 

Testing CodeAnalysisEngine:

 

 

The first two statements create the mock objects to use in the actual call. The next line sets the expectation to load the provided assembly file. The implementation of the SUT:

 

 

In the next test, I added the assembly parser in the constructor which prompts a change in the first test to take in another argument.

 

 

And the implementation now calls the Parse method on the assembly parser.

 

 

The next step is to unit test the CodeAssemblyLoader. This class is responsible for loading the assembly file using Cecil, so the test expects a call to Cecil's AssemblyFactory.GetAssembly method. Unfortunately AssemblyFactory.GetAssembly is a static call which makes it impossible to test. If it is Ruby then I can easily mock it but that's another thing :).

 

And, here is the implementation of CodeAssemblyLoader class

 

 

Testing CodeAssemblyParser is also not easy because of the same AssemblyFactory issue so I'm going to load the actual assembly but mock the repository to expect the add method.

 

 

Line no. 17 is loading the actual assembly. Then I mocked the repository and set it to the assembly parser. In the end, I set the expectation and call the method to verify.  The Parse method internally calls the AddAssemblyData method with the assembly definition.

 

 

So the unit tests are passing, I still need to implement the dependency injection configuration to run the application.

 

Setting up Castle configuration for dependency injection:

 

There are two different dependencies which we need to resolve here. First, CodeAnalysisEngine depends on the CodeAssemblyLoader and CodeAssemblyParser which it takes in as constructor arguments.

 

 

Second, to resolve the dependency in assembly parser for assembly data repository which is a property.

 

 

After configuring the dependencies, I can run the application select the TestCases.dll file which we created in part II.

 

 

To confirm if it works by opening the codestore.db file in the database browser.

 

 

Source Code: http://code.google.com/p/code-store/

 

Conclusion:

So, this completes the first story which is about storing the information about the assembly into the database. I also looked into interaction based testing and used the Moq framework to implement mocks inside the tests. The assembly parsing is then implemented using Cecil.

 

As always, your suggestions and comments are always welcome to improve the project.

No Comments