Quickly identify the impact of making changes to the system under development

Models in any form and created at any point during the development process are often beneficial. With models, you have the ability to:

Better understand and communicate at a high level what the system should do

Divide and conquer large, complex problems through clarity of data, relationships, and workflows

Utilize model information in developing code

In this article, we will walk through an example problem that utilizes TDD, while also incorporating a model oriented approach, and in doing so combining the advantages of TDD and utilizing models.

Please pardon the code used in this example. The code is overly simplistic and the example contrived. The focus of the article is not on the quality of the code, but on the approach and how you might apply the approach for your own projects.

I am hoping that this article will generate some discussion that can be used to further refine the approach and example outlined in this article. I don't have a strong background in applying TDD directly on many actual projects, and I very much welcome input from those in the TDD community.

A future article topic may cover behavior driven development (BDD).

Background

Mo+ will be the choice for incorporating a model oriented approach in this article, since Mo+ is specifically designed for such an approach. The Mo+ model oriented programming language and the Mo+ Solution Builder IDE for model oriented development was introduced in this Code Project article. The intent of this article is to demonstrate how model oriented development can be applied to TDD. Mo+ models and templates will be presented, but this article is not a Mo+ language or tool how to. However, you can apply the technique outlined in this article with other modeling and code generation tools to various degrees. If you are trying to use Mo+, the above article also has a Learn More section with additional information to get you started.

The example will be implemented using C# for the code and Visual Studio unit tests for the tests.

The Process Defined

Where you combine test first development (TFD) with steps to refactor your code that still passes all of your tests.

Model driven development (MDD) generally implies that you create the front end architecture with models in one form or another, and translate (usually more complex) models into code. That is not what we are going to do here. So, I'd like to coin a term, model oriented development (MOD), that allows a simple, focused model to be created and used for development purposes at any point in the development process. With MOD, you are free to use as little or as much of the focused model for development, and only when you want it.

Finally, I'll coin another term, model oriented test driven development (MOTDD) that will be the process we will use here. MOTDD can be described with this formula:

MOTDD = TDD + MOD

With MOTDD, you consider refactoring your test cases and code in a model oriented way in addition to refactoring your code as per standard TDD. Consider the following diagram, where the top half represents TDD, and the bottom half represents incorporating MOD:

This process will be further explained through the example below, but you probably have a couple of questions right now. Why would I want to apply MOD to TDD? And when would I want to apply MOD to TDD?

Why Apply MOD?

Applying MOD is useful when you can use it to enhance the refactoring goals and steps that you already practice with TDD. MOD is useful when you can meet your goals more quickly and safely than with a custom approach alone.

When to Apply MOD?

You apply MOD when the information in your model becomes known and available, and when you recognize model oriented patterns that can reduce the amount of your custom code, and make your system more robust to changes based on the model and on team best practices.

The goal of applying MOD is not to generate as much model oriented code as possible, it is to meet your overall goals you consider when refactoring. You may find opportunities in your refactoring to replace duplication in model oriented code with robust, model independent custom code. And, that's a good thing!

The Example

We want to implement a simple e-commerce scenario utilizing the Northwind database merely as a source for model information. Although the model can contain much more information, we are only going to utilize entities and basic properties for the model oriented code this example.

Consider a Change: What do we want this system to do? Let's start with some simple user stories:

Customer registers

Customer finds a product

Customer orders a product

Customer receives product

Getting Started In A Test First Manner

Write a Custom Test: We know our system has something to do with products, orders, and customers. To get started, let's make tests out of the above user stories and make them pass (we'll do them all at once here for brevity). Our customer tests look like:

OK, our unit tests pass. See the code for Step 1 in the sample download.

Custom Refactoring

Make a Custom Code Change: We look at our mostly fake code written to get the tests to pass, and we realize we need to store customer, product, and order data in some way. So, let's just create a fake repository that stores lists of this data:

Refactor Custom Code:Let's use this "repository" in our customer class methods. We see some potential problems in OrderProduct() and GetProduct() to address later, but at least we can modify RegisterCustomer() and FindProduct() to use the repository:

Our tests immediately fail due to null references errors in the repository.

Make a Custom Code Change: We need to change the way our tests are run to get them to work. So, we'll add a Setup() method that is called before each test is run to initialize the repository with a product and a customer:

OK, our tests run now! See the code for Step 2 in the sample download.

Some Simple Model Oriented Refactoring

Using the Northwind (SQL Server or MySQL) database as a model source, we review the model and how information there impacts our overall design. The following diagram illustrates some of the model information in Mo+ for entities and properties of interest for our example:

We immediately see missing properties in our Customer, Product, and Order model classes, and a required order structure to allow for multiple products in an order. But, hold your horses! We are doing a test driven approach, we are not going to try to incorporate all of this information yet.

Refactor Model Oriented Code: We do notice that we can easily refactor our Repository class in a model oriented way to make it robust with respect to future changes in the model. I'm lazy, I think I can get away from writing a model oriented test first, since the code after model oriented refactoring should be virtually the same as before.

Refactoring model oriented code involves creating or modifying a code template (or more than one), and generating updated model oriented code. Following is a Mo+ code template for the main body of the Repository class (Mo+ template code will be displayed as images only for readability, actual code for the complete templates can be found in attached sample download). At line 16, the foreach statement iterates through each Entity in the solution of interest for adding to the repository, and at line 18 we are creating a repository for a particular entity, inserting EntityName information from the model. In our model, we are tagging interested entities with "ForDev" that we can use to limit our repository only to entities of interest right now:

Our unit tests still pass. See the code for Step 3 in the sample download. Note that generated files in the sample are named with "_G" to quickly identify them. The project level Mo+ code template for this step is called RepositoryCode.

A Little More Towards Handling Orders

In thinking about this little e-commerce scenario, we realize a couple of things that we need to address:

A customer often logs in later to order

A logged in customer places the order and retrieves order information

Consider a Change: So, we add one more user story:

Customer logs in

Write a Custom Test: We create a unit test for this user story, and update our order product and get product tests to be based on the logged in customer:

Our unit tests still pass. See the code for Step 4 in the sample download.

Incorporating Model Structure

We now want to utilize some of the overall customer, product, and order information found in our model. Incorporating a fair amount of model oriented code with custom code created with a test driven approach may seem daunting. But, as with our previous steps, we start with some unit tests and get them to pass.

Write a Custom Test: Let's start with a simple custom unit test. We know we want to hold all of our information in the repositories, and for each type of object, we need to be able to add them to the repository. So, let's start with this ProductCRUDTests class to test adding a Product to the repository:

Write a Model Oriented Test: Writing a model oriented test involves creating or modifying a code template (or more than one) for a test, and generating updated model oriented test code. First we utilize our ProductCRUDTests class as the starting point for a template to add tests for each entity in the model. The Mo+ template for the test class body looks like the following (where we insert EntityName information from the model):

Next, with this template we generate the additional unit tests for each entity in the model. The ProductCRUDTests class body should be identical to the custom one we just made. The OrderCRUDTests class for example looks like the following:

We can now delete the custom ProductCRUDTests file since it is no longer needed. But of course, the additional unit tests such as OrderCRUDTests fail, since our corresponding model classes do not have add methods. So, it is time to refactor our model classes in a model oriented way!

Refactor Model Oriented Code: Now we want to do some model oriented refactoring for our model classes. To refactor our model classes, we use the Product class as a starting point for the template. We want the generated model class to contain the model properties and the add method. The Mo+ template for the model class body looks like the following (where we add information for each Property as shown in line 11, and we insert PropertyName and EntityName information from the model):

Next, with this template we generate the model class code for each entity in the model. The generated code for the Product class looks like the following:

OK, everything compiles now, and also the unit tests pass! See the code for Step 5 in the sample download. The entity level Mo+ code templates for the CRUD unit test and model code are called CRUDTestCode and ModelClassCode respectively.

Utilizing Model StructureTowards Handling Orders

Now that we have incorporated the actual model structure, we can take another look at how we handle ordering. But first, let's make some custom changes to utilize our newly generated methods to add items to repositories. We replace direct calls to add items to the repositories in CustomerTests.Setup() and Customer.RegisterCustomer() (you can see changes in sample download). We run our unit tests again and they pass.

Now, let's take a look at our support for ordering given what we know now about the model structure. The unit test for placing an order is CustomerTests.TestOrderProduct():

Write (modify) a Custom Test: We know that an order should not have a product name, but should have a set of order details containing product related information. If the customer is ordering one product, this unit test should look like:

Write (modify) a Custom Test: We spot a couple of things wrong with the test, knowing that a customer should only be able to "get" a product that is part of a valid order for that customer. So this unit test should look more like:

At this point, you can also remove the ProductName property from Order. The unit tests pass again, and we have something that almost looks like an ordering process (though I wouldn't put any of my $ into it). See the code for Step 6 in the sample download.

Continue To Iterate Until You Are Satisfied

Of course this example is far from a real e-commerce system. In practice you will continue to interate in writing tests to cover your user stories, writing custom code to make them pass, refactoring custom code, and refactoring model oriented tests and code.

We will do one more iteration here.

Customize A Model Oriented Test: Let's beef up our AddProduct() test to add multiple products and verify that ids are unique (and rename method to TestAddProduct() while we are at it):

Refactor Model Oriented Code: But we customized some model oriented code and need to refactor the model oriented unit tests and model code. To refactor, it is just a matter of updating the associated templates and regenerating the model oriented code.

See the code for step 7 in the sample download that shows the updated templates and updated model oriented code.

In Summary

Hopefully this article has provided you with some food for thought in considering model oriented development in conjunction with test driven development. Please provide some feedback if you would like to see additional clarity and refinement in the process description and example.

In addition, I hope you try Mo+ and the Mo+ Solution Builder to fully utilize incorporating a model oriented approach to your development. The free, open source product is available at moplus.codeplex.com. In addition to the product, this site contains sample packs for building more complete models and generating complete working applications. Video tutorials and other materials are also available at this site. TheMo+ Solution Builderalso contains extensive on board help.

Become a Member!

The Mo+ community gets additional support, and contributes to the evolution of Mo+ via the web site at https://modelorientedplus.com. Being a member gives Mo+ users additional benefits such as additional forum support, member contributed tools, and the ability to vote on and/or contribute to the direction of Mo+. Also, we will be running monthly contests for members, where you can win $ for developing solutions using Mo+.

If you are the least bit interested in efficient model oriented software development, please sign up as a member. It's free, and you won't get spam email!

Share

About the Author

I enjoy coding like an excellent beer. My particular passion and experience lies in the realm of modeling and code generation. In the late 80s and early 90s, I was involved in early modeling and code generation tools that reached the marketplace, including a tool that modeled FORTRAN programs and generated FORTRAN for parallel supercomputer architectures, and a tool that managed Shlaer-Mellor models and generated C++ code. Over the years, I have applied Shlaer-Mellor, UML, and custom modeling and various code generation techniques to greatly benefit the development of enterprise applications.

My current passion and endeavor is to foster the evolution of model oriented development. In particular, I am passionate about evolving the Mo+ model oriented programming language and related model oriented development tools, with as much community input as possible to achieve higher levels in the ability to create and maintain code. The open source site is at moplus.codeplex.com, and the Mo+ membership site is at modelorientedplus.com.