Chapter 12. Unit Testing

Unit testing is a key technique used by developers to maintain a healthy and robust code base. The approach allows developers to write smaller tests that invoke more varied permutations of a given method or a unit of code. Treating each method as a distinct testable piece of code means that not only current usage of that method is safer from regression, future usage is protected as well. It frees the developer to focus on more permutations, such as error scenarios and parameter values beyond those currently in use.

Unit testing is different from integration testing where many method invocations are tested as a part of an overall business process. Both have a place on Force.com. In this chapter we will explore when to use one over the other.

To understand how to adopt unit testing we first need to understand dependency injection. This is the ability to dynamically substitute the behavior of a dependent class' s methods with test or stub behavior. Using a so-called mock implementation of dependent methods (such as reading from the database) avoids the need for the repeated process intensive setup of test data. We will explore the ways in which dependency injection can be implemented, including the use of the Apex stub API that is included with the Force.com platform.

Finally, this chapter shows how unit testing can be applied to the Apex Enterprise Patterns introduced earlier in this book. Using the popular open source ApexMocks mocking framework with the Apex stub API, developers can write unit tests for controller, service, domain and selector classes.

This chapter will cover the following aspects of unit testing:

  • Exploring the difference between unit and system testing
  • Understanding dependency injection and how to implement it
  • Leveraging the Apex stub API
  • Applying the ApexMocks mocking framework to FormulaForce

Comparing Unit testing and Integration Testing

Much of the difference between unit and integration testing relates to the scope of the code being tested and the goals of the test. Chances are you have been mixing a combination of the two on Force.com without realizing it.

First, let's consider this definition of Integration testing by Wikipedia:

Integration testing (sometimes called integration and testing, abbreviated I&T) is the phase in software testing in which individual software modules are combined and tested as a group. It occurs after unit testing and before validation testing. Integration testing takes as its input modules that have been unit tested, groups them in larger aggregates, applies tests defined in an integration test plan to those aggregates, and delivers as its output the integrated system ready for system testing.

In general, most published guidance on writing Apex tests falls into this integration testing category. Indeed, the only way to cover Apex Trigger code is to physically write to the database. There is also the need to test declarative aspects of your solution (unit test only tests code) and how these affect the broader application processes.

When I read the preceding Wikipedia description, it described the Apex tests I had been writing before discovering how to write true unit tests on this platform (more on this later). Visually my tests would look like the following diagram.

Comparing Unit testing and Integration Testing

I would write Apex test code to set up my records or execute my controller code or Service layer code (to test the API). This code would update or query the database accordingly.

Then I would write test code to query the results records to assert the behavior was what I was expecting. This approach would ensure that all the classes involved in the functionality: such as service, domain, unit of work, selector and domain—got executed. It also ensured that they did just what they needed to in the scenario I was testing to result in the overall goal of completing the end user or API process being tested.

Integration tests are still necessary to test your key processes and indeed to obtain code coverage on Apex Triggers required to package your solution. However, they do have some downsides:

  • Execution time: Since each test method needs to set up, update, and query the database executing them, all or individual test methods can start to take increasingly longer to execute, slowing the developer's productivity.
  • Scenario coverage: Introducing more varied scenarios can become less practical due to the preceding performance concern. This discourages developers from doing thorough testing (we hate waiting!). This in turn reduces code coverage and the value of the tests. And this ultimately increases the risk of bugs being left undiscovered by the developer.
  • Code coverage challenges: Obtaining 100% code coverage is the desirable goal, but it can be impossible to achieve in certain scenarios. For example, error handling for some system events cannot be emulated in an integration test.
  • Ensuring future proofing and reliability to other developers: Methodologies like Scrum teach us to write code incrementally and just in time for market needs. However, developers are often aware of future needs and wish to build frameworks, engines or libraries to support both current and future needs. It may not be possible to write integration tests that ensure that when that time comes such code is robust and reliable for other developers.

Introducing Unit Testing

As we will discover throughout this chapter, unit testing helps address the preceding challenges. When used in combination with integration tests, the resulting test suite creates a much stronger and more efficient set of Apex tests for your application.

Learning about and writing unit tests can often seem difficult because it requires you to learn other concepts, some of which determine how you write your application code. Things such as Separation of concerns, Dependency Injection, Mocking, and Test Driven Development are concepts you will come across when Googling about unit tests. We will be looking into these throughout this chapter. Of course, you are already by now quite familiar with SOC and how this is applied to Apex Enterprise Patterns.

Consider this definition of unit tests by Wikipedia:

Intuitively, one can view a unit as the smallest testable part of an application. In procedural programming, a unit could be an entire module, but it is more commonly an individual function or procedure. In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method. Unit tests are short code fragments created by programmers or occasionally by white box testers during the development process.

The biggest difference between unit and integration testing is evident through this definition. Unit tests may cover methods on potentially any class in your codebase, not just the outer controller classes or service classes. Furthermore, the goal is to test the execution and behavior of only the code in the chosen method and nothing else.

The hard part can be preventing the execution of other methods that the method being unit tested calls. This is done by substituting parts of the real production code (within the test context only) with stubbed code, or what is referred to as mocked code, that emulates the behavior of those methods. Then the method you're unit testing can still execute. Ideally, the method should be unaware of this substitution, or injection as it's known.

For example, a unit test for a Controller method should test the behavior of that method only, such as interaction with the view state or the handling of UI request and response with the Service layer calls. What the service layer method does is not of interest. Developers should not have to consider any requirements of dependent methods like this (for example, database setup) in the unit test code.

Likewise, when testing the Service layer logic, its use of the unit of work, selector and domain classes are not of interest or concern. However, these dependencies need to be resolved. The next section discusses how to inject dependencies.

The following diagram shows a series of unit tests focusing on a specific class and using mock alternatives of the dependent class methods to avoid having to concern themselves with their needs:

Introducing Unit Testing

A mock version of a dependent class should emulate the expectations of the calling method (the method you are testing), by returning a value or throwing an exception for example. Configuring mocking is a key setup aspect of a unit test you need to master. It can allow you to write some very sophisticated and varied tests. We will go into more detail on mocking later in the chapter.

Note

Meanwhile, if you're not familiar with the mocking approach when developing the test code, this http://en.wikipedia.org/wiki/Mock_object link is a useful introduction. Though naturally there are many other sites on the Internet describing its benefits. The following is an extract from the Wikipedia page:

'In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.

In a unit test, mock objects can simulate the behavior of complex, real objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test. If an actual object has any of the following characteristics, it may be useful to use a mock object in its place."

The more developers invest in unit tests; the less likely integration tests and your end users are to find defects. Keep in mind, however, that proving methods work in isolation does not prove your application works as designed. Integration tests are still a critical tool in testing your application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.53.93