Showing posts with label chemistry. Show all posts
Showing posts with label chemistry. Show all posts

Sunday, June 19, 2011

Writing and testing a 2007 MS Word Add-in using NUnit, Moq, and the MVP pattern

Hey everyone,

Today I want to show you an approach to writing a testable UI. To do this I'm going to write a 2007 Microsoft Word Add-in using Microsoft's Visual Studio Tools for Office. The goal is to write an application level add-in and test it using NUnit and Moq.

 I'm going to design it around the Model-View-Presenter pattern: We'll have two interfaces: An IView, which will be an Office ribbon and contain no logic. Then we'll  have an IPresenter, which will handle all of the "UI logic" such as taking actions based on user dialog results etc.

Our Presenter won't actually have any domain logic in it: we'll be delegating that to another class (I'll explain that in a minute). This means that when we test it, we'll be doing behavior testing instead of testing for object states. This also means we'll be using Moq to setup our Mock domain objects and verifying that calls are made to them.

Of course it's important to note that this kind of testing involves some coupling between your tests and the implementation of the class under test, but I think it's acceptable since the Presenter is behavior based.

So what kind of add-in will we be doing? Well at first I was going to be boring and use a very contrived example. But then I came up with a nice domain-specific idea.

I figured why not do something chemistry related? That's always fun. So we're going to make an addition to the Word ribbon that allows the user to enter either a chemical formula or an entire reaction equation. When the user clicks the "Action" button, the add-in will either render the equation as an image and insert it, or if the user enters a single compound it will insert the molar mass at the cursor position.

Here's the user interface:


Don't worry about the actual logic behind rendering and molar mass calculation. I've already taken care of that using my chemical database program I wrote last year. Now of course I think the code is crap, but it still works and that's the main thing.

So let's get started by defining our interfaces. Our IPresenter will only contain a single method: Action() which returns void and expects no parameters. Our Ribbon will implement IView. To start with, we'll give IView two properties: MolarMass and EquationText:


IView.cs:
namespace Chemistry.UI
{
    public interface IView
    {
        double MolarMass
        {
            set;
        }
        string EquationText
        {
            get; 
        }
    }
}
IPresenter.cs:
namespace Chemistry.UI
{
    public interface IPresenter
    {
        void Action(); 
    }
}

In our first iteration we'll simply assume that the user-entered text is a compound and we want to calculate the molar mass of it. Let's write a simple test that will confirm our IPresenter implementation makes a call to our domain object (an instance of IFormulaParser):

Presenter.cs:
namespace Chemistry.UI
{
    public class Presenter : IPresenter
    {
        public void Action()
        {
            throw new NotImplementedException();
        }
    }
}
IFormulaParser.cs:
namespace Chemistry.UI
{
    public interface IFormulaParser
    {
        double MolarMass(string compound); 
    }
}
And now our test file, PresenterTests.cs:

using NUnit.Framework;
using Moq;
using Chemistry.UI; 
namespace Tests
{
    public class PresenterTests
    {
        private IPresenter _presenter;
        private Mock<IFormulaParser> _formulaParser; 

        [SetUp] 
        public void SetUp()
        {
            _presenter = new Presenter();
            _formulaParser = new Mock<IFormulaParser>();            
        }

        [TestCase] 
        public void Action_UserEntersCompound_CallsMolarMassMethod()
        {
            _presenter.Action(); 
            _formulaParser.Verify(parser => parser.MolarMass("O2")); 
        }
    }
}

So in our test class we initialize a new Presenter object and a mock FormulaParser. For now we've set the IFormulaPresenter to return a value of 32 when MolarMass("O2") is called. In our test case, we make sure that our FormulaParser is called correctly. Of course the test fails since Presenter doesn't do anything. Let's change it so that it uses the text entered by the user instead our hard-coded value.

using NUnit.Framework;
using Moq;
using Chemistry.UI; 
namespace Tests
{
    public class PresenterTests
    {
        private IPresenter _presenter;
        private Mock<IFormulaParser> _formulaParser;
        private Mock<IView> _view; 

        [SetUp] 
        public void SetUp()
        {
            _presenter = new Presenter();
            _formulaParser = new Mock<IFormulaParser>();            
            _view = new Mock<IView>(); 
        }

        [TestCase("O2",32.0)] 
        [TestCase("H2", 2.0)]
        [TestCase("C6H6",78.0)]
        public void Action_UserEntersCompound_CallsMolarMassMethod(string compound, double molarMass)
        {
            _view.Setup(view => view.EquationText).Returns(compound);
            _formulaParser.Setup(parser => parser.MolarMass(compound)).Returns(molarMass);                 

            _presenter.Action(); 

            _formulaParser.Verify(parser => parser.MolarMass(compound)); 
        }
    }
}


Now we've done a couple things. First, we've created a new mock IView. We also changed our test to accept parameters and set up the view's EquationText property with the compound string. Finally, we check again to make sure the presenter calls the FormulaParser.MolarMass method. Now let's make the Presenter pass the test.

public class Presenter : IPresenter
{
 private IFormulaParser _formulaParser;
 private IView _view;

 public Presenter(IView view)
 {
  _view = view;
  _formulaParser = new FormulaParser(); 
 }

 public void Action()
 {
  _view.MolarMass = _formulaParser.MolarMass(_view.EquationText); 
 }
}

Alright, so now the Presenter is using the IView's equation text property, calculating the molar mass, and setting the IView's MolarMass property to the result. Let's also update the test cases to verify that View's MolarMass property is also set:

[SetUp] 
public void SetUp()
{
 _view = new Mock<IView>();
 _formulaParser = new Mock<IFormulaParser>();                  
 _presenter = new Presenter(_formulaParser.Object, _view.Object);                  
}

[TestCase("O2",32.0)] 
[TestCase("H2", 2.0)]
[TestCase("C6H6",78.0)]
public void Action_UserEntersCompound_CallsMolarMassMethod(string compound, double molarMass)
{
 _view.Setup(view => view.EquationText).Returns(compound);
 _formulaParser.Setup(parser => parser.MolarMass(compound)).Returns(molarMass);  
              
 _presenter.Action(); 

 _formulaParser.Verify(parser => parser.MolarMass(compound));
 _view.VerifySet(view => view.MolarMass = molarMass); 
}

At this point, our test passes. And at this point if we implement IView in our Ribbon object, everything should work. So let's proceed and implement rendering equations as well. Let's start by writing a test to make sure our rendering object is called. To do thish we need to add our IEquationRenderer interface, which has a reference to System.Drawing:

public interface IEquationRenderer
{
 Image RenderEquation(string equation); 
}

And modify the Presenter constructor:

public Presenter(IEquationRenderer equationRenderer, IFormulaParser formulaParser,  IView view)
{
 _view = view;
 _formulaParser = formulaParser;
 _equationRenderer = equationRenderer; 
}

Finally add our test case:

[TestCase("H2 + O2 ----> H2O")]
public void Action_UserEntersEquation_CallsEquationRenderer(string equation)
{                        
 _view.Setup(view => view.EquationText).Returns(equation);            
 _presenter.Action();
 _equationRenderer.Verify(renderer => renderer.RenderEquation(equation));
 _view.VerifySet(view => view.EquationImage = It.IsAny<Image>()); 
}

An important note here: I wasn't able to mock the Image interface that we're passing around, so we're verifying that the IView gets its EquationImage property set to "any" Image object instead of a dummy Image that we would pass around normally.

If we make this test pass by just adding a call to our EquationRenderer, we could also still be calling the molar mass calculation which we don't want. So let's add another test that ensures that when we parse an equation, we don't try a molar mass calculation:

[TestCase("H2 + O2 ----> H2O")]
public void Action_UserEntersEquation_DoesNotTryMolarMassCalculation(string equation)
{
 _view.Setup(view => view.EquationText).Returns(equation);
 _presenter.Action();
 _formulaParser.Verify(parser => parser.MolarMass(It.IsAny<string>()), Times.Never()); 
}

This test will fail because we need some conditional logic to differentiate compounds and equations. Let's do this by adding a method to IFormulaParser:

bool IsEquation(string text); 

Let's set this up in our equation tests to return true by adding this setup line to our two test cases:

_formulaParser.Setup(parser => parser.IsEquation(equation)).Returns(true); 

The actual implementation will just check for the presence of "---->," but that could always change. Now we can modify the Presenter to take this into account:

public void Action()
{
 if (_formulaParser.IsEquation(_view.EquationText))
 {
  _view.EquationImage = _equationRenderer.RenderEquation(_view.EquationText); 
 }
 else
 {
  _view.MolarMass = _formulaParser.MolarMass(_view.EquationText);
 }            
}

And now our tests pass. At this point, the rest of the work is implementation details. Here's an example of the final result:

The important thing to note with this approach is that we've created an extremely testable UI layer. Since all of the logic is in the Presenter class, we can test all aspects of the UI. This becomes even more exciting when your UI logic involves the user making decisions by answering prompts and responding to dialogs. You could for example wrap a call to MessageBox.Show in an IView DisplayMessage() method that returns a DialogResult. This would allow you to test the Presenter and just mock the IView.

Anyway, just for reference, the full source code of this add-in is on my website. Apologies in advance for the weird project structure, the Test project is under the "Tests" folder. The solution uses Visual Studio 2010.

Monday, April 4, 2011

More on chemical structure drawing with C#, TDD, and a dash of algorithms

Progress 

So recently I've been working on implementing a chemical structure drawing tool in C# using WinForms. Along the way I've been adhering to TDD (test-driven development), and it's been a pretty positive experience. I've managed to avoid using the debugger and have a very clear separation between UI and logic code.

The "industry standard" for representing chemical structures is to represent atoms as nodes and bonds as edges of an undirected graph. Although atoms and bonds can have an extensive set of details, I've been sticking to a set of simple properties in the meantime. So far the back-end of the program is fairly developed, and on the GUI front-end I have basic functionality such as creating and deletion of atoms/bonds.

Quirks 

There's always a random quirk that you end up having to implement, in my case it was allowing the user to click on a bond. Now, a bond is just a connection between two (x,y) points, so to test for a mouse click on a bond we need to test the distance of the mouse position from the line segment representing the bond.

Oddly enough, I had a working C# solution to that problem awhile ago (it was a pain to find one too, adapted from this stackoverflow question!)

But the other 'quirk' is a lot more interesting than geometry, and it involves generating a string of text that represents a chemical compound. One of the important formats to do this is called SMILES, and I would eventually like the program to be able to generate and/or parse it.

The problem? The format is kind of complicated and hairy, and has to account for a lot of different chemical properties. So for the moment writing a parser is a task in itself.

Generation using depth-first searching

Luckily, generating the SMILES code is easier. The most simplistic way is to simply do a depth-first search through the graph, while identifying nodes that appear twice (these will be part of rings). This algorithm will generate "correct" but not canonical or normalized SMILES strings, although the programs I've used will all parse them.  To get more involved requires identifying rings, taking into account other bond/atom properties, and all kinds of hairy stuff.

In fact, in the big chemistry libraries (in Java for example), the code to do this is very long and hard to understand. At least, I can't understand it. So for the moment I'm stuck trying to reinvent the wheel, but starting a whole lot simpler.

And that's all for today. Check back next time for more progress and other fun topics.

Sunday, March 27, 2011

Chemical structure drawing with C#/VB.NET and TDD

So a little fact about me: I picked up chemistry back in 2007 and I really enjoy the subject*. Naturally I like to apply software development to chemistry-related problems (see my chemical database for an example).

I figured "hey Chris, you haven't made your own structure drawing tool yet," and it would be a good fun learning experience. Right now I'm using a C# back-end for all of the logic and plan on using a VB .NET UI front-end just to pick up the language. Also, I've really been trying to get more out of TDD and practice it more. I've tried to stick to it and so far it's going pretty well, although there is room for improvement.

I also finally decided to pick up AnkhSVN, which provides integration of Subversion into Visual Studio. So far it's working pretty well.

Anyway, here's some of the things I hope to implement:
  • Basic editing: adding/deleting of atoms and bonds
  • Support for adding fragments at once instead of having to create them each time
  • Saving/loading of structures in some common formats
  • (possible) integration into my chemical database program since it currently relies on an external editor
We'll see how far that goes, hopefully I'll have some screenshots too. Unfortunately it will probably be slow progress since I'm being bombarded with assignments this week (chemistry professors here seem to like homework as opposed to projects, which CS professors and I prefer...)


*My favorite experiment was distilling liquid bromine from KBr/H2SO4/H2O2 solution ... good times. One of  my general chemistry professors even helped me improve the process