Integration Testing
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:
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):
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)
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.
1603602377 says
http://www.louisvuittonpurses-bags.net Louis Vuitton Purses
http://www.louisvuittonhandbags-lv.com LV Bags
http://www.cheaplouisvuitton-bags.net Cheap Louis Vuitton
http://www.louisvuitton0.com Louis Vuitton
http://www.louisvuittonoutletsun.net Louis Vuitton Sunglasses
http://www.louisvuittonhandbags-lv.com Louis Vuitton Handbags
http://www.coachsoutletonline.org Coach Online Outlet
http://www.coachfactorysoutletonline.org Coach Factory Online
http://www.coachsfactoryoutlet.net Coach Factory
http://www.coachsoutletstoreonline.com Coach Outlet
http://www.alouisvuittonhandbags.com Authentic Louis Vuitton
http://www.burberryscarfoutlete.net Burberry Outlet
http://www.coachsoutletonline.org Coach Factory Outlet Online
http://www.e-louisvuittonoutlet.net Louis Vuitton Outlet
http://www.burberryscarf-outlet.com Burberry Outlet
http://www.coachfactoryoutletonlinstores.net Coach Factory Outlet Online
http://www.cheapcoachsbags.net Coach Bags
http://www.louisvuittonpurses-bags.net Louis Vuitton Bags
http://www.chanelbags-max.com Chanel Bags
http://www.louisvuittonbagsonline.net Louis Vuitton Bags
http://www.louisvuittonoutletsz.com Louis Vuitton Outlet
http://www.burberrybags-sale.net Burberry Bags
http://www.coachfactorysoutletonline.org Coach Factory Outlet Online
http://www.chanelhandbags-bags.net Chanel Bags
Coach Factory Outlet says
http://www.buycoachfactoryoutlet.net Coach Factory Outlet
Coach Factory Outlet says
http://buycoachfactoryoutletsz.com