Integration Testing

by Donn Felker 24. October 2008 03:03

Integration Testing

gear-bevel Purpose: Testing how the layers/tiers/objects integrate with each other.

When we're unit testing, we're testing a single unit of code. We're testing to see if an addition operation results in what we expect it to.  Does five plus five equal ten? Does this complex calculation f(x) result in y when x = 5? These are the things we're after with unit testing.

With integration testing our goal is to test whether or not our components integrate together. Do they speak (interact) as we would expect?

Example 1: If the "CustomerFacade" (Facade Layer) class interacts with the "CustomerService" (basically, a rules Layer) when we perform the action "CreateNewCustomer", does it work the way we expect it to?

Example 2: If the "CustomerService" (A Service Layer Implementation) must access a remote web service, what is the result? What is the result if the network is down? What is the result if the service is up but is throwing exceptions for every call (perhaps the interface changed). These are integration tests.

A More Refined Definition: At a very basic level integration testing is the combining of two units that have already been tested into a component where the interface between them is tested. In this sense a component is an aggregate of more than one unit. Think of this as two pieces of the application. Perhaps the Customer Facade and the Customer Service. Both handle completely different things (Facade handling the orchestration of the commands, Service handling the execution of a particular command and the order of operations). Each can be tested individually but what happens when we test them together as they are actually going to work within a given system? We now have Integration Testing.

 

Lets look at a quick example with some diagrams to help solidify this concept.

Diagram of System:

 

What We Want to Test: 

image

 

The End Goal of Integration Testing: To identify performance, functional and reliability of the given integration.

 

What we can identify with Integration Tests:

  • Performance/Reliability: If we run 1000 iterations of the same test, does performance degrade? What about 10,000 iterations? What about 100,000? 1M?
  • Incompatibility: The interface between these components may be incorrect. This will usually occur very early in development of the code or in test development.
  • Pattern/Principle Usage: Testing components together can help identify areas in which may need to be refined more. If you realized you're object is doing a price calculation, a customer age verification and a shopping cart total, you may be doing too many things. Look at the Single Responsibility Principle and the Law of Demeter.
  • Component Functional Testing: Are the objects functioning as we have designed them?

 

Example of what we want to test:

In this legacy example we want to test the actual interaction between Legacy and Customer Service. I want to make sure that these two classes work with each other as expected. Our legacy code would look like this:

    public class CustomerFacade
    {
        public void UpdateCustomer(Customer customer)
        {
            CustomerService service = new CustomerService(); 
            service.SaveCustomer(customer);
            
            // Do some other work ... 

        }
    }

We need to re-factor this as we have done in our Unit Testing example. Doing so will turn this class into something  like this:

 

public class CustomerFacade : ICustomerFacade
{
    private ICustomerService customerService;

    public CustomerFacade(ICustomerService customerService)
    {
        this.customerService = customerService;
    }

    public void SaveCustomer(ICustomer customer)
    {
        customerService.SaveCustomer(customer);
        // Do some other work ... 
    }
}

We also need to re-factor the CustomerService class as well...

 

public class CustomerService : ICustomerService
{
    private ICustomerRepository customerRepository;

    public CustomerService(ICustomerRepository customerRepository)
    {
        this.customerRepository = customerRepository;
    }

    public void SaveCustomer(ICustomer customer)
    {
        customer.EditDate = DateTime.Now;
        customerRepository.Update(customer);
    }
}

 

The reason we re-factored this is because the CustomerService object had a dependency on the CustomerRepository. Using the Extract Interface pattern we are able to eliminate the concrete dependency with an interface. We will be able to mock the repository with a mocking framework like Rhino Mocks or TypeMock Isolator.

This is very important: The reason we are going to mock the repository is because we are trying to perform an integration test on the Facade and Service layer. I DO NOT want to test the repository integration. That is ANOHTER INTEGRATION TEST.

Examples of Testing:

In this test we are going to ensure that the CustomerFacade calls the CustomerService which in turn calls the CustomerRepository. If the CustomerRepository does not get called this means that there is something wrong with our integration because CustomerService calls into CustomerRepository. This is what our "integration expectation" is.

 

        
[Test]
public void Customer_Facade_Should_Not_Throw_With_A_Valid_Customer()
{
	MockRepository mockery = new MockRepository();
	ICustomer customer = mockery.Stub<ICustomer>();
	ICustomerRepository repositoryMock = mockery.DynamicMock<ICustomerRepository>(); 
	
	// Lets make sure that the Repository got called. 
	// Review the Fluent Syntax here: http://ayende.com/Wiki/Comparison+of+different+Rhino+Mocks+syntaxes.ashx
	With.Mocks(mockery)
	.Expecting(() => 
	{
		// Lets make sure that repository gets called. 
		// If this DOES get called, the test will succeed. If it does not
		// the test will fail, indicating that our integration test is not working. 
		// Which means we have a problem in how our objects are working together.
		Expect.Call(delegate{repositoryMock.Update(customer);}); 
	})
	.Verify(() =>
	{
		ICustomerService service = new CustomerService(repositoryMock);
		ICustomerFacade facade = new CustomerFacade(service);
		facade.SaveCustomer(customer);
	}); 
}

 

Now lets update the CustomerService class to throw if the customer age is under 18. Now this isn't the best architecture for domain logic, but this is for a simple example. If I were to go down a full Domain design example the details of the test would get lost in the details of the classes.

Here's the new Customer Service:

 

public class CustomerService : ICustomerService
{
    private ICustomerRepository customerRepository;

    public CustomerService(ICustomerRepository customerRepository)
    {
        this.customerRepository = customerRepository;
    }   

    public void SaveCustomer(ICustomer customer)
    {
        if(customer.Age < 18)
        {
            throw new ApplicationException("Only customers over the age of 18 can purchase. Sorry!"); 
        }
        customer.EditDate = DateTime.Now;
        customerRepository.Update(customer);
    }
}

 

Now lets write our test ...  This test should thrown an Application Exception if the customers age is under 18.

 

        
[Test]
public void Customer_Facade_Should_Throw_With_An_InValid_Customer()
{
	MockRepository mockery = new MockRepository();
	ICustomer customer = mockery.Stub<ICustomer>();
	customer.Age = 13;
	ICustomerRepository repositoryMock = mockery.DynamicMock<ICustomerRepository>();

	// Lets make sure that the Repository got called. 
	// Review the Fluent Syntax here: http://ayende.com/Wiki/Comparison+of+different+Rhino+Mocks+syntaxes.ashx
	With.Mocks(mockery)
	.Expecting(() =>
	{
		// Lets make sure that repository DOES NOT get called. 
		// If this DOES get called, the test will fail.  
		// This should never get called because the test should throw before 
		// we get to this point. However, if it does not throw, this will make the test
		// fail. 
		Expect.Call(delegate { repositoryMock.Update(customer); }).Repeat.Never();
	})
	.Verify(() =>
	{
		ICustomerService service = new CustomerService(repositoryMock);
		ICustomerFacade facade = new CustomerFacade(service);
		facade.SaveCustomer(customer);
	});
}

If we get our expected result, we should see an exception thrown. This is what the exception should look like (click for larger):

image

If for some reason the logic in the CustomerService is bypassed, our Expect.Call on the repository should catch the test and fail the test. Here's what happens when this occurs. (Click for larger image)

image

Now that we have our expected integration results - an exception we need to make the test pass. Any time we throw an exception in a class and its expected, we need to add the "ExpectedException" for NUnit. Now our test looks like this:

 

	[ExpectedException(typeof(ApplicationException))]
        public void Customer_Facade_Should_Throw_With_An_InValid_Customer()
        {
            // ... Test stuff (removed for brevity). Its the same as above.
        }

 

Notice the ExpectedException Attribute on top of the method. This informs the unit test framework that we are expecting an exception to be thrown. This is "expected behaviour".

 

Conclusion

We've covered the key differences between Unit Tests and Integration Tests.

Unit Tests - To test an individual unit of code to ensure it is working properly.

Integration Tests - Testing how layers/tier/objects integrate with one another.

Regardless if your doing unit testing or integration testing, its a positive step towards cleaner, more maintainable code. Knowing the difference can help you identify what types of test you have written and types of test you may need to write. Just because you've tested all of your classes at the unit level does not mean that all of them are going to integrate properly. writing integration tests help do this.

Follow the motto: Fail Early. Fail fast.

Unit and Integration tests help do this, very quickly.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Testing

4 Common Follies Made In Unit/Integration Testing

by Donn Felker 3. October 2008 09:19

I was having a discussion with a colleague last week and we got on the topic of unit testing. What unfolded is what stemmed this post... It started like this...

Colleague: "There is no need to test getter's and setter's in a property, because 'I know they work'."

Me: "WHOA WHOA WHOA!! You know they work? How? Please inform me, because I KNOW my code works too, but I also know I MAKE MISTAKES, A LOT OF STUPID MISTAKES... "

The conversation continued and eventually spawned this post ... but before we get started, I'd first like to say I've been burned by all four of these follies, so I know first hand how important they are.

  1. Not Testing Components
  2. Using a shared database
  3. Testing code that depends on external objects
  4. Writing a test stub and not finishing the test

 

1. Not Testing Components

"I don't need to test that, I know they work." - you, me, everyone (at one point in time) :)  

Why its a Folly: A few questions in regards to this should set us up nicely ... Why are you even trying to perform testing of any kind if you're not testing even the basic stuff? This is the REAL SIMPLE stuff that should not be ignored. Seriously, If you write code and you KNOW its solid, then why write tests for it if your so confident? Think about that... That doesn't make sense! WE'RE ALL HUMAN, WE ALL MAKE MISTAKES. That's why. No one's code is perfect. I don't care who you are. Also, what happens after you check in your code and then tomorrow Developer X checks out the code, makes a change to a setter and then checks it back in. How do you know if that did or did not break anything? YOU DON'T.

Example:

Your original code - a simple read only property that returns the order total:

public double OrderTotal { get; } 

 

Developer X's code after he changed it. He added the new "customer level pricing discount" feature that was requested by your boss.

private double orderTotal; 
public double  OrderTotal 
{ 
	get 
	{ 
		if(customerLevel == CustomerLevel.Platinum)
		{ 
			orderTotal = TotalOrderAfterCustomerDiscountAppliedFor(customerLevel); 
		}
		return orderTotal; 
	} 
}

 

Unfortunately, because no test was written to act as a parachute to catch us (to make us fail early) we are now possibly introducing a bug into the system. Also we have no unit test backing up the new business functionality. Unfortunately because "you know it works" doesn't cut it in this case. The QAT team fires up the app and notices the system has some "odd behavior" at certain times of execution. A bug is submitted and you eventually trace down the source of the error to the property accessor change. Oops.

 

What To Do: Test your properties. Its easy, its quick and it will save your *ss in a bind. Trust me, I've been that guy that said "I know this works, its part of the framework." . If you want to make life real easy on yourself you can even set up a ReSharper Template for it. Its not that hard.

 

Notes: For example - I'm not saying to go out and test that the FileInfo class actually does what it says. If your code or the framework offer the ability for a bug to exist, then test for it. Regarless if its there OR NOT. It's your parachute. You don't see skydivers skimping out on their equipment do you? They do have an EMERGENCY pack. Yeah, THEY KNOW their main pack works. But what if it doesn't? We all need a back up plan to help us out.

 

 

2. Using a  Shared Database

At times you will NEED to connect to a database to perform some type of integration testing.

 

Why its a Folly: Most teams that I've worked have begun working from a shared "dev" database. This introduces so many problems with the dev workflow environment that only an example is the best way to describe the madness going on here.

It usually goes like this (you've probably seen this before too):

  • DevA updates a SprocX on the Dev DB server
    • Changes are validated by "running the app"
  • DevB updates a table definition on the Dev DB Server
    • Changes are validated by "running the app"
  • DevA develops some new functionality, runs app to debug and then it breaks on some code that he DID NOT CHANGE.
    • He gets a SQL Exception stating that column "CustomerNo" is not found in SprocX.
      • What? Huh? I just validated this 20 minutes ago. WTF?!
      • He looks at the code, talks to his peers, finds out that DevB had to make a change for Feature123 that made him change the column name to "CustomerNumber".

It's complete madness working with a shared model. DB Objects get overwritten, changed, updated, you name it - it WILL HAPPEN. Murphy loves to come visit during this time. The amount of un-managed change is too much to handle for any team. Following a shared model usually creates far too much friction for any team to succeed in any time boxed environment (and who doesn't live in one of those).

 

What To Do: Implement a Database Sandbox for each developer. In this example we provide a separate development or test database for each developer.

Note: Managing changes in a database is a challenging task all in itself. This can be intensified in any multi-developer environment. Read K. Scott Allen's series on version control for a database.

  1. Three Rules for database work (note the first bolded topic in this post... )
  2. The Baseline
  3. Change Scripts
  4. Views, Stored Procedures and the like
  5. Branching and Merging

 

 

3. Testing Code that depends on external objects (network, file, web service)

Continuing with the previous item, when writing unit tests that depend on an external service (in this case - a database) states that you are making the following assumptions: 

  1. The Network is up and working
  2. The remote machine your connecting to up and running
  3. The database server is running
  4. The database server is accepting remote connections
  5. The database in question is ACTUALLY ON THAT SERVER
  6. The connection string is correct
  7. No one has messed with what you're about to test
  8. and ... the planets align correctly

We're assuming that the database is in the correct state. What if this is a shared environment? What  if someone changed something? What if the network hiccups? Trust me, if it can happen, it will happen. I know first hand because all of this has happened to me.

 

Why this is a folly: Again, you're betting on the fact that 1-7 above (if not more) are going to succeed. What if this is a web service? What types of things are you betting on there?

  1. The network is up
  2. The the Internet connection is up
  3. The remote server is up
  4. The web service address is correct
  5. The web service interface has not changed
  6. The web service in face does what you expect it to do
  7. blah, blah, blah - too much!

The same follies again! A file is no different. Heck, even another class can be this way. Imagine working with a class that requires HttpContext to be available? It would be a mess to try to reconstruct this object graph to ensure that my test result was the same.

 

What To Do: This is a lot harder than it sounds - break your dependencies. Testing is not the only thing you'll get from this. You'll get flexibility, functionality and the ability to become more agile with your software. How do you break your dependencies... implement some dependency injection techniques into your code. After you break these dependencies you can mock them out with fake implementations to streamline your testing.

 

4. Writing a Test Stub & Not Finishing The Test

I'll leave the easy one for last... this one is easy to fix. :)

Ok, I'll be the first to admit, I'm guilty of this from time to time. However, I have become older and wiser and I have now started implementing an initial not implemented exception throw upon the test getting created.

Here's what I'm talking about when we forget (this test will pass):

[Test]
public void Customer_must_be_21_to_drink_booze()
{
	ICustomer customer = new Customer("FirstName", "LastName"); 
	// TODO: Finish Test. Ran out of time this afternoon! :) 
}

 

Why this is a folly: Its not a completed test. It does not do anything.

 

What to do: Here is what I do to eliminate this (this test will fail):

[Test]
public void Customer_must_be_21_to_drink_booze()
{
	throw new NotImplementedException(); 
}

 

 

 

Conclusion

We all make mistakes, I hope that this post helps at least a few people NOT make these mistakes. :)

Currently rated 4.7 by 3 people

  • Currently 4.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | TDD | Testing

Unit Testing - It's not what most of you think ...

by Donn Felker 19. September 2008 08:37

Unit Testing

Purpose: To test an individual unit of code to ensure that it is working properly.

This is where the most confusion lies, so I'm going to focus the majority of the beginning series on this area. Possibly to the effect of a few posts.

Normally you'll hear a developer say "I have unit tests for this" but in reality the tests are actually integration tests. 

At a very basic level integration testing is the combining of two units that have already been tested into a component where the interface between them is tested. In this sense a component is an aggregate of more than one unit.

Basically - if you're code has outside dependencies, and you're testing the main calling interface (maybe a method), you are probably testing the entire stack beneath the method, in turn testing how the code integrates together. AKA: Integration testing. A full post on integration tests is coming soon...

The trouble comes with understanding how to test individual units of code without testing their dependencies. Code that has tight coupling suffers from problematic testing and is very brittle and challenging to change.

Here is an example snippet of a system via a diagram for reference:

image

At a high level, here's how the application works:

image

Lets look at some legacy code that every single one of us have written at some point in our lives. This will be the code that we are trying to test. This code is located inside of the "CustomerService" shown above.

        public void SaveCustomer(Customer customer)
        {
            customer.EditDate = DateTime.Now;
            var repository = new CustomerRepository();
            repository.Update(customer);
        }

 

Can you write a unit test for this?  No. (Well, that's not 100% true. You can, kind of. But you'd have to be using a tool like TypeMock to do so - which in this exact example would be cheating in regards to OOP in this instance. More on this at a later time.) In this case - to ensure a unit test was secured around this code you'd have to do the following:

  1. New up an instance of Customer Service
  2. Create a customer
  3. Ensure the CustomerRespository Works (who knows what that means, maybe you didn't write it)
  4. Be sure that everything the customer repository touches in the "Update" method works as well (again, who knows what it does, maybe you're not the developer who wrote it)

The code for the above would look something like this:

        [Test]
        public void UpdateCustomeShouldCallUpdateOnResponsitory_Legacy()
        {
            CustomerRepository repository = new CustomerRepository();
            CustomerService service = new CustomerService(repository);
            Customer customer = new Customer();
            service.SaveCustomer(customer);
        }

 

At this point you've already introduced too many dependencies into your code. If any of these outside dependencies break - our test breaks. In this case our outside dependency is the CustomerRepository. These outside dependencies could be a network connection, a file, a database, a web service, anything. Those are all things we mare NOT trying to test. We're trying to TEST one unit of code, the update call on the repository. I'm wanting to make sure that the customer repositories "update" method gets called with the customer inside of it. I don't actually want the repository code to be called thought. I just want to know that it was called. That's all I care about. I'll write another test to ensure that the value of the customer's edit date was updated properly at a later time. I'll write more tests to ensure that the customer repository works the way it should.

 

How can we fix this?

We can test this unit of code if we follow some basic OOP principles. Mainly dependency injection.

Here's the code refactored. It is far from perfect, but this code will allow you to test in isolation of outside dependencies:

public class CustomerService : ICustomerService
{
    private ICustomerRepository customerRepository;

    public CustomerService(ICustomerRepository customerRepository)
    {
        this.customerRepository = customerRepository;
    }
    public void SaveCustomer(ICustomer customer)
    {
        customer.EditDate = DateTime.Now;
        customerRepository.Update(customer);
    }
}
In this case we can inject our dependencies (customer repository and customer) as interfaces. In the test through the use of a mocking framework we can then mock out and stub our calls to the customer repository and call expectations on them. The test would look like this:
    [TestFixture]
    public class CustomerServiceTests
    {
        MockRepository mockery = new MockRepository();
        private ICustomerRepository customerRepositoryMock;
        private CustomerService customerService;
        private ICustomer customerStub;

        [SetUp]
        public void Setup()
        {
            customerStub = mockery.Stub<ICustomer>();
            customerRepositoryMock = mockery.DynamicMock<ICustomerRepository>();
        }


        [Test]
        public void UpdateCustomerShouldCallUpdateOnRepository()
        {
            With.Mocks(mockery)
            .Expecting(delegate
            {
                Expect.Call(delegate { customerRepositoryMock.Update(null); })
                .IgnoreArguments().Repeat.AtLeastOnce();
            })
            .Verify(delegate
            {
                customerService = new CustomerService(customerRepositoryMock);
                customerService.SaveCustomer(customerStub);
            });
        }
    }

At this point we're only testing one unit of operation, therefore this is a "unit test".

Testing code that is similar to the legacy code shown above is not going to be a unit test. If you were to wrap a test around the legacy code you would be creating an integration test.

It will "look" like a unit test but what its doing is actually integration testing. This is because you would be inferring that all the other pieces of the code are going work as expected.

This is not necessary true. Trust me ,I've been down this road.

 

Problems arise when you begin to make these assumptions about your dependencies. In my experience I've had the following happen:

  • The database is down - TEST FAILS
  • The database is in an unknown state - TEST FAILS
  • The network is down - TEST FAILS
  • The file is not available - TEST FAILS
  • The file is locked - TEST FAILS
  • The file is read only - TEST FAILS
  • The SMTP Server is not up and running - TEST FAILS
  • The web service is not responding - TEST FAILS

 

All of these instances also bring up good problematic areas that you should test for. Utilizing proper OOP and Unit tests will allow you to simulate these events so that you can code for them in your application.

 

 

Review

 

What we are not doing here:

  • We are not testing the whole stack from top to bottom.
  • We are not testing two classes together.

 

What we are doing here:

  • Testing one logical piece of code.
  • Executing a Unit Test.

 

Popular Testing Frameworks:

Popular Mocking Frameworks:

 

Recommended Reading:

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

TDD | Testing

Testing For Developers Series

by Donn Felker 19. September 2008 08:25

Over the next few weeks I'm going to cover some topics will help alleviate the pain of understanding the difference between different tests in application development.

The series will consist of the following articles:

Disclaimer: I've had to explain this so many times that I figured I might as well type it out on my blog and send people the link to save myself time and to give friends a place to bookmark for reference. Also, these topics carry quite the heavy controversial and opinionated crowd - to the point where some people have vastly different opinions on what each of these topics actually mean (and some people WILL disagree with me and I'm ok with that). These are my opinions that I have gleaned from my years in the field utilizing various testing methodologies.

As a developer, when you're first getting into testing your code via a testing framework you will start to notice that testing terms are thrown around like they're ingredients in code soup. Unit, integration, functional, acceptance, regression, load, etc... WHOA, hold the phone buddy. What the ...?!?! Confusion sets in immediately. You probably have an idea of "what" you want to do but you're not sure of the actual name for it.

A lot of time developers will say "yeah, there are unit tests backing it up" when in fact those unit tests are actually  repeatable "integration tests". This is completely fine if this is what they meant to do. But unfortunately they don't know what they are saying and they're probably confusing themselves as well as their peers. I'm hoping this series will act as a reference for some people who are just getting started in testing from a developer standpoint. By no means is this a be-all/end-all series for testing, it is just meant as a springboard for developers just getting started. Testing (TDD in general) has a huge learning curve as it requires quite the paradigm shift to get started. This series will be refined as I find ways to improve it and it will be updated throughout time.

The end goal of this series is to help newcomers to the testing world identify the differences between the types of testing. I will also provide some examples of how to test in each of these scenarios (some more than others) and I will provide information about some of the tools that you can use in each of the testing categories.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

TDD | Testing

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Donn Felker

Senior Consultant
MCTS
ScrumMaster
Agile Practitioner

About Me | Books I Recommend

Gotta Pay The Bills


Tag cloud

    Popular Posts

    RecentComments

    Comment RSS

    Calendar

    <<  December 2008  >>
    MoTuWeThFrSaSu
    24252627282930
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    View posts in large calendar