As a result, you can't call doSomething without calling TheirEntity's static method. This code is hard to test because static methods are impossible to mock in Java.
So, how do you get rid of this form of Static Cling and get that small test to pass? You can use a technique sometimes known as the Repository Pattern, a form of Abstract Factory. Create an interface and an implementation with the unmockable static method calls:
interface TheirEntityRepository { TheirEntity selectById(int id); // other static methods on TheirEntity can be // represented here too } public class TheirEntityStaticRepository implements TheirEntityRepository { public TheirEntity selectById(int id) { // non-static return TheirEntity.selectById(id); // calls static method } }
Next, inject a TheirEntityRepository into MyObject and use it instead of calls to the static method:
public class MyObject { private final TheirEntityRepository repository; public MyEntity(TheirEntityRepository arg) { this.repository = arg; } public int doSomething(int id) { return repository.selectById(id).getSomething(); } }
You can do this even if you don't have access to source code for TheirEntity, since you're not changing the source itself, but merely encapsulating its static methods in an injectable interface. The techniques shown here generalize to the case where a static method acts as a Factory of objects.
Now you can inject different implementations of the repository for different tests, such as "never finds anything," "always throws an exception," "only returns a TheirEntity if the id is a prime," and so forth. These kinds of tests would've been impossible before this refactoring.
Code Preview - The examination and execution of created tests
Test Reporting - The examination of test results.
The figure below shows the Code Generation tab of the GME Test Suite Creator. It displays a hierarchical view of the tabs and allows the user to select which tags to include in the sample tests. The sample test is generated from a skeleton test modeled after the documentation example scraped from the code.google.com website. This was a nice idea which added testing of the documentation to the process.
An interesting problem that these types of automatic test generation frameworks can encounter is the combinatorial explosion of generated tests. For example, if each tag attribute can have 8 possible values and a sample mashup contains 10 tags, enumerating each combination would take roughly 1 billion (810 = 230) tests! To address this, the team created an Options dialog box that would allow the user to specify different test suite sizes in addition to the test suite name and type. A further refinement, allowing the user to select which set of specific values to use for tag attributes would have been implemented if the team had more time.
The next figure shows the Code Preview tab of the GME Test Suite Creator. It shows the list of tests created under a given test suite and allows the user to manage and execute the test suite.
Finally, the Test Report Tab shows the results of the tests executed under Selenium-RC.
GME Test Suite Creator was itself written in Python and hosted on Windows, Linux and MacOS. The team presented and demonstrated the GME Test Suite Creator to faculty and other student/industry teams as part of the UC-Irvine ICS Student Show Case. Over the next few weeks I will be kicking the tires and evaluating the battle worthiness of the GME Test Suite Creator delivery which included source code and a complete set of documentation.
I certainly had a wonderful time interacting with the team and participating in this program!
The GME Test Suite Creator Team (from left to right: Gabriela Marcu, Peter Lee, Michelle Alvarez, Jason Dramby and George Pirocanac)
Solution Although there is no 'one size fits all' solution, there are some basic principles we can use to solve the testing problem for web applications:
Invest in integration tests (identify the smallest sub-system)
Separation of Concerns (don't do the set-up through the interface you are testing)
Test each interface separately (mock out everything that you are not testing)
Consider dependencies in production (figure out how dependencies can fail, and test that)
Use a mix of strategies and tools. There is no silver bullet.
And no... you cannot scrap all of your end-to-end tests
A recipe for testing goodness Using the principles above we can build up a recipe for testing web applications. In the second part of our blog we will walk through each of these steps for a real web application.
Explore the system's functionality
Identify the system's architecture
Identify the interfaces between components
Identify dependencies and fault conditions
For each function:
Identify the participating components
Identify potential problems
Test in isolation for problems
Create a 'happy path' test
End note: value of a test A question commonly asked by developers when writing tests is, "is this really worth my time?" The short answer is "always!". Since fixing a bug is way more expensive than preventing it in the first place, writing good tests is always worth the time.
While there are many different classifications of tests, the most common way of classifying them is based on their size and the areas of a product they test. Each test answers specific questions about the product:
Unit test: is the method fulfilling its contract?
Small integration test: Can two classes interact with each other?
Medium integration test: Is a class interacting properly with all its dependencies? Does it anticipate and handle errors correctly? Are the needed functions exposed on an API/GUI?
Sub-system test: Can two sub-systems interact with each other? Does one of them anticipate all errors of the other and does it deal with them appropriately?
System test: Does the entire system behave as expected?
Keeping this questions in mind, testing at various levels allows us to write more focused and meaningful tests. Remember that effective tests are those that provide quick and useful feedback, i.e. quickly identify issues and pin point the exact location of the issue.
In the next episode we will walk through the recipe proposed above using a real web application.