Showing posts with label unit-testing. Show all posts
Showing posts with label unit-testing. Show all posts

Monday, November 21, 2011

Get up to speed on a new project by writing Selenium tests

Alright. So I've taken the task of maintaining and extending an ASP .Net MVC 2 project with a friend of mine. It's not too big, but there are some things working against us:
  • Neither of us have done ASP .Net development before
  • We had to set up a new database (this was actually the hardest part) 
  • There are no tests for the project
  • The javascript looks like our javascript back in the beginning of the Recordings project ... we've learned better since then.
Actually, let me emphasize that last point. I'm a big fan of automated testing, so much that I feel hesitant to modify code that isn't under test (excluding the simple stuff, of course). Especially on an unfamiliar code base. That's why one of the things we've been doing and really emphasizing on this project is automated UI tests with Selenium 2 WebDriver.

Writing tests using Selenium has helped us learn the functionality of the application as well as establish a good suite of tests for future modifications. And at the moment they are more useful than unit testing due to the difficulty of unit testing parts of the code.

In addition, we're making sure to keep our tests nice and clean using PageObjects and also making them reproducible when they mess with the database. Eventually we'll have these tests picked up by our CI server when it builds the project before deployment.

Of course, that doesn't mean we're going to go without unit testing the code. There are a couple static classes from the ASP .Net libraries that are mixed in with areas we'd like to test -- but using some questionable hacks we've been able to work those out. And after burying said hacks in another layer of abstraction, we can write nice clean unit tests like we all enjoy.

So: Now that we're on a new project things should pick up with this blog again. Recordings is live and complete, so there hasn't been much programming for a few weeks.

See you next time, though.


    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.

    Tuesday, April 26, 2011

    More servlet testing with Mockito and JUnit

    Hello everyone,

    I had some time to implement some more functionality for The Project today, so I thought I'd share more with you about testing our servlets.

    Last time we were testing a server's JSON response to make sure it called the right validators and handled bad inputs.

    Today we're testing the response of a POST request (also via AJAX). Our target: A simple controller that handles some very basic security (sufficient for our purposes). We want to send a request to the server and set a session variable if the user is authenticated. We are using JUnit integrated into Eclipse.

    So first, in our TestAdminController*:

    import static org.junit.Assert.*;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.verify;
    import static org.mockito.Mockito.when;
    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.Mockito;
    

     Naturally we'll have more than one test case, so we will put some of the common code into a setup() method that is run before each test case is executed:

    private AdminController controller; 
    private HttpServletRequest stubRequest;
    private HttpServletResponse stubResponse; 
    private HttpSession stubSession;  
    
    @Before
    public void setup()
    {
        controller = new AdminController();     
        stubRequest = mock(HttpServletRequest.class);
        stubResponse = mock(HttpServletResponse.class); 
        stubSession = mock(HttpSession.class);  
    }
    
    

    As always we've stubbed the request and response. This time we're also stubbing a session since we'll be setting a session attribute.

    Now we set up one of our test cases: simply testing that, given a valid user, the session variable is set by the server. To do this we first hook up our HttpServletRequest so that when the server attempts to retrieve the current session, it returns a stub instead. We also setup a stub user manager (which handles authentication) so that we don't have to worry about usernames and passwords and all those things:

    when(stubRequest.getSession()).thenReturn(stubSession);
    when(stubRequest.getParameter("password")).thenReturn(UserManager.SUPER_SECRET_ADMIN_PASSWORD); 
    UserManager stubUserManager  = new UserManager(); 
    when(stubUserManager.userIsAuthenticated(Mockito.anyString(), Mockito.anyString())).thenReturn(true)
    

    We use Mockito.anyString() because we don't care what credentials have been retrieved, but we always declare the user as valid. This decouples the test from the UserManager class.

    Finally, we are ready to initialize the controller:
    AdminController controller = new AdminController();
    controller.setUserManager(stubUserManager);
    controller.doPost(stubRequest, stubResponse);         
    

    To ensure that the server set the session variable, we use verify() to determine that the HttpSession.setAttribute() method was called:

    verify(stubSession).setAttribute("authenticated", true);
    

    Now we can run the test and if everything is working, it should pass. In this way, we have ensured that at least some parts of the controller are working so any problems will be on the client.

    Well, that's all for now!

    *I put the imports here because I hate it when I find a solution to something, but the author doesn't list the imports and the library isn't easily searchable!

    Thursday, April 21, 2011

    Unit testing java servlets with Mockito and JUnit

    Hey everyone,

    SO last time I shared with you about experimenting with code coverage tools. I've been considering code coverage now when writing new functionality and adding tests. One thing I've noticed is that our controllers (servlets) have no test coverage. Now I know you might say "but Chris, you shouldn't be having eg. domain logic in your controllers that need testing anyway!"

    That's true guys, I agree. And we really try and maintain strict separation between the domain and controllers. Consider our testing goals for our controllers as the step before functional testing (via HtmlUnit for example). We just want to make sure that when the controller sends off the response, nothing is broken before it gets to the view.

    To accomplish this, we can use a mocking framework (such as Mockito) to stub out the HttpServletRequest and HttpServletResponse interfaces that the servlet get/post methods contain.

    Our target: a controller used to handle AJAX comment posting requests. When the user posts a comment, the server needs to:
    1. parse the parameters and make sure they're all valid 
    2. make sure the user hasn't tried to post too many comments in a short amount of time 
    3. validate and sanitize the fields in case they contain malicious code (eg. javascript) 
    4. create a comment and persist it to the data layer
    5. send the new comment object back as a JSON object for display 
    The controller doesn't actually do most of the work: it delegates it to the domain/data layers for validation and persistence. But we can still test to see if the servlet actually generates a correct response.

    Let's say we want to check to see that the controller is doing its job sending a potential comment to the CommentValidator before it tries to post it.  We do this by sending a request with invalid parameters (such as a blank user name field) to the servlet.

    First for our imports:
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.when;
    
    Then we create our stub HttpServletRequest object:
    HttpServletRequest stubRequest = mock(HttpServletRequest.class);
    
    Now we add the expected method calls/return values to the stub request. One parameter that the server should use is "type" specifying what kind of comment is being posted. So we can indicate that when the server retrieves the "type" parameter, it can return one of our choosing:
    when(stubRequest.getParameter("type")).thenReturn("recording");
    
    We do this for the rest of our comment's fields. Now we need a way to determine if the server validated the comment or not. In our case, the controller will send a JSON representation of a CommentValidationResult (which contains a message and an error type). For a blank text field, the error code will be BLANK_FIELD.

    So let's test for the presence of BLANK_FIELD in our response (which will be a JSON string). To do this we create a StringWriter and a PrintWriter and then inject them into a stub response:
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);        
    HttpServletResponse stubResponse = mock(HttpServletResponse.class);       
    when(stubResponse.getWriter()).thenReturn(pw); 
    
    Here we create a stub HttpServletResponse and inject our new PrintWriter so that when the servlet writes to the response's output stream, it will write to our StringWriter instead.

    Now we call the method on the controller and make sure it contains the BLANK_FIELD:
    controller.doGet(stubRequest, stubResponse);
    String result = sw.getBuffer().toString();
    assertTrue(result.contains("BLANK_FIELD")); 
    
    We can run the test now and it will tell us if the controller sent back an error due to our comment having blank fields (as it should have done).

    We can use this approach to test how the server responds to weird URL strings that are sent by mean people to disrupt our website in addition to testing for normal functionality. Maybe next time we'll try and test the view layer...


    *Oh, and I finally integrated syntax highlighting courtesy of SyntaxHighlighter

    Monday, April 18, 2011

    Code coverage, Hibernate integration, and other fun things.

    Hello everyone,

    Lately our project has been progressing very nicely. My colleague had written a small program to parse raw data into an SQL-friendly form, so we are not restricted to test data anymore. This has the nice side-effect of boosting motivation because we are dealing with the Real Thing now.

    My focus lately has been on adding a comments section to the web app. In our schema, we have a few different comment tables for different types objects. Luckily in the Java code we can just make all of them derive from a BasicCommentBean so we can be lazy. I've made the comments section as generic as possible and self-contained.

    With this setup, you can reference comments.jsp and set the required attributes (a comment data source namely) and everything else is handled for Free by CommentController. We also perform server-side validation on the user input and sanitize it via the apache.commons.lang library to prevent malicious code execution on the client. Oh and we have some nice ajax and animation going on courtesy of jQuery...

    Now that we're using real data, we've integrated Hibernate. Oddly enough, the process was surprisingly painless, but I wouldn't be care-free just yet... I don't have a ton of experience with Hibernate, but so far it's pretty straightforward once the mappings are working correctly.

    One last think: I've been experimenting with code coverage via the EclEmma plugin for eclipse. It's nicely integrated and I've used it to see how much code coverage our unit tests are providing (63% ... looks like room to improve). Over the next few days/weeks/etc. I hope to use it more extensively to improve our tests and coverage of the important parts of the project.

    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.

    Saturday, March 12, 2011

    Implementing interaction between jQplot and java servlets

    Today I'm going to share my experiences developing a way to let users plot graphs of certain interesting data.


    Way back when, we originally were going with the Google chart API to accomplish this. It's simple and it looks good. However, we decided instead to take a jQuery approach (for learning and to avoid making a call to a second server) with jqPlot. jqPlot is a free graphing library which generates the graph on the client side and accepts data in a convenient format.

    After playing around with it, we were able to develop a nice implementation:

    Now aside from a few things that need to be worked out (such as fractional years), it looks good and was very simple to implement. Our approach to this using java servlets was:
    • Client sends an AJAX graph query to the GraphsController class with a parameter ("getgraph") set to true to signify a graph request
    • Servlet gets request, builds a GraphRequestBean and validates it. If validation fails the server sends back an error object using the JSON library for Java called google-gson
    • Servlet sends request to a GraphsManager, which generates the Graph by interacting with the data layer. 
    • Graph is sent as a JSON object and is read by the javascript, where a call to the plotting library is made. 
    • User gets nice looking graph
     We also added some error handling and the ability for users to regenerate the graph through a link. I'm pretty excited about this implementation (and jQuery in general...) because it worked very well. And also I have even more respect for unit tests because they saved a lot of time debugging, since I was constantly changing code around while developing.

    Thursday, February 17, 2011

    What happens when you just set a Stream position to zero (C#)

    This morning I was fixing a few bugs which I had found in HTML Combiner and I noticed a few of my unit tests that loaded configuration files were failing. They were using a MemoryStream object so that they read from a string instead of the file system. The first thing I thought was that it was just a problem reading from a string, and wouldn't happen when reading from an actual file.

    Of course then I ran the program and notice that when I loaded the configuration file for my website, my tags were showing up twice. After setting up NUnit to run in visual studio and stepping through the debugger, it was in fact reading the file multiple times.

    After spending about an hour wondering what was wrong, I casually glanced at a few links on google about resetting a stream position (which I do at the beginning of the method). Finally, I made it to the StreamReader.DiscardBufferedData example code on the MSDN library website.

    If you change the stream position using Seek, the actual position of the buffer is now out of sync. So you have to call DiscardBufferedData in order to force the stream to go to the desired position. After I added this line everything worked once again:

    sr.DiscardBufferedData();          // where sr is a StreamReader
    sr.BaseStream.Seek(0, SeekOrigin.Begin);

    This made me glad that I've been adding unit tests to catch things like this -- I might have overlooked the problem the tests weren't failing. Another similar incident happened a few days ago when a small bug was introduced after I made a change, the bug would result in the build path not being loaded from a configuration file. The actual problem looked pretty innocent, it would have taken time to isolate the cause. However, I was able to catch it because the test failed shortly after I had made the change.

    Tuesday, February 15, 2011

    Unit testing a method that makes heavy use of FileStream (C#)

    While working on the HTML Combiner, I ran into a situation where I was trying to test a method that wrote a configuration file. This method makes heavy use of FileStream, and so the resulting test would have been coupled to the filesystem. This seems like a common issue you would run into, and there are a few ways to approach the unit test for such a method:
    1. Allow the filesystem dependency
    2. Create an "IDataSource" interface and make an implementation that wraps the FileStream object
    3. Instead of using FileStream, use its parent, Stream, in the method you're trying to test
    Option (1) is out since that's exactly what we're trying to avoid! Allowing the dependency on the file system is a bad idea, because it allows anything that can go wrong with file IO to potentially fail your unit test at an unpredictable time. In addition, if you're writing to files, you have to worry about cleaning up so your tests don't read the file of a previous test run.

    Option (2) seems like a good idea because you can stub the IDataSource interface in the unit test. In fact, I probably will do this later on (I admit, I was rushed for time!). This means that you will have to change your method's code to use the interface instead of changing "FileStream" to "Stream," but it's a more elegant solution that does not depend on Stream at all.

    Option (3) is the one I went with, and isn't a terrible solution. By using a Stream object in the target method, you no longer depend on the FileStream (and consequently the file system). In your test for example, you can pass in a MemoryStream which contains a test "file" whose contents are in a string you declare. However, I did encounter some disadvantages:
    • You still have a dependency on Stream, instead of using a stub
    • You have to specify the encoding your string uses in order to create a MemoryStream out of it
    • Creating and writing to the MemoryStream is not painless
    Well, we still achieve decoupling from the filesystem, which was the main goal. However, in the future, I will probably switch to an "IDataSource" interface and use that. This way, we get total independence from Stream and we could also write an implementation that, say, reads/writes an SQL database.

    Saturday, February 5, 2011

    Forcing mandatory unit tests when pushing to the central repository

    For the past few days I've been working on getting our build process automated to deploy to a local server (since we have no remote host as of yet). It's deceptively simple:
    1. build java source files (including tests)
    2. copy compiled class files to deploy directory
    Sounds easy, right? It should be, but my relative inexperience with web apps translates into spending extra time configuring environment variables, searching out libraries and putting them in the right place, etc. But hey, that's finished now!


    The real issue I wanted to tell you about is related: We want to make sure our unit tests are run each time someone tries to push to the production branch of our central repository, to make sure they haven't broken. I figure we also want to test them outside are local environment, let's say the directory of the local server we test on.
    So we add a preoutgoing hook to mercurial that simply launches an ant script that will run the unit tests and the push will fail if the tests fail. To get this working though, my process resembled this less-than-ideal workflow:
    1. build java source files (including tests)
    2. copy compiled class files to deploy directory
    3. call junit task from within Ant
    4. Observe that _x_ is not in the right place
    5. Put _x_ in the right place
    6. Repeat
    Where _x_ usually refers to hibernate and one of its xml files. Figures. However, after hours of pain and realizing that I had pointed the ant script to the wrong version of Tomcat, this process now resembles the ideal process at the beginning. I'm pretty happy with it, I just hope it continues to work...