Kindle Notes & Highlights
by
Cedric Beust
Read between
March 22 - April 10, 2019
designing applications and components for testability.
Michael Feathers’ book Working Effectively with Legacy Code (Prentice Hall, 2005),
rule of thumb for deciding whether you should throw a checked exception: Can the caller do something about this exception? If the answer is yes, you should probably use a checked exception; otherwise, a runtime exception is a better choice.
principle in software engineering in this short example, called the DRY principle (Don’t Repeat Yourself).
important software engineering principle known as the Single Responsibility principle)
Whenever you run a suite that contains errors, TestNG will automatically generate a file called testng-failed.xml in your output directory (by default, test-output/). This XML file contains a subset of the original testng.xml with only the methods that failed.
How do we verify that our code is thread safe? There is no easy answer to this question. Actually, there is no definitive answer to this question, either. Past a certain size, it’s impossible to prove with 100% certainty that a given portion of code will behave as expected if several threads access it simultaneously.
Mocks test the behavior and interactions between components, whereas stubs replace heavyweight processes that are not relevant to a particular test with simple implementations.
The rule of thumb is that if we want to test interactions between components, mocks might be a better approach than stubs. Mock libraries allow us to specify the interactions in a concise and exact manner. Stubs, on the other hand, are more useful as scaffolding—dependencies that components expect to be present and to perform certain roles. Stubs should be used for secondary components used by the component under test. The test purpose in this case is to test the primary component itself, rather than its interactions with other components.
three of the more popular coverage tools. Clover by Cenqua is a commercial offering generally regarded as the best in the field. EMMA and Cobertura are both open source solutions that work reasonably well.
Unit tests: A unit test tests an individual unit in the system in isolation. Unit tests run very quickly since they have little to no start-up costs, and almost no external dependencies. Functional tests: A functional test focuses on one piece of functionality. This usually involves interactions between different components. Integration tests: An integration test is an end-to-end test that exercises the entire stack, including any external dependencies or systems.
When capturing a bug that’s found in production code, it’s also important to label it correctly. The requirement that should always be satisfied is this: “If someone new joins the project six months after I leave, will he or she be able to look at this test case and know why it’s here?” Comments in the test should include a link to the associated bug report. If
remain pragmatic when it comes to determining how much the implementation details should be exposed. Making all methods public is obviously going too far, using package-protected is preferred where possible, but then leaving everything private and making testing much more awkward is probably going too far the other way.
Testing for specific strings in an HTML response is very fragile, as tests will start breaking due to the most trivial of HTML changes that have no impact on functionality or even presentation.
Language
Avoid checked exceptions. Runtime exceptions are perfectly useful for this sort of thing. Declaring them in the method signature as well as documenting in the Javadocs ensures that the caller can decide whether to handle the exception or let it bubble up. If the caller chooses the latter, there’s no longer a need to explicitly catch the exception, so it’s one less place botch it up. Wrap exceptions. Wrapping exceptions is fine if you are absolutely sure that by the time a stack trace is printed, the underlying cause is shown. This is important since by the time such an exception is thrown,
...more
there are cases where checked exceptions are indeed useful. When making the decision, ask this question: “Can the caller do something about the exception?” In this case, doing something involves a nongeneric handling, such as deciding to retry, presenting the user with options on how to proceed, or trying an alternative call when this exception is raised.
Test methods that access shared immutable state are independent of each other.
Sharing mutable state makes sense only if you know the order in which the test methods are invoked.
It doesn’t matter how you create your tests as long as you do create them.
Test for private method