not just integration tests
Why don’t software engineers only use integration tests? Provide examples of circumstances in which other testing strategies are important.
you need unit tests to do test-driven development (where you write the tests and then code until the tests pass). integration tests don’t test the behaviour of functionality in isolation which restricts the degree to which a single unit can be tested. having unit tests encourages healthy abstraction principles and maintaining loose coupling. also, integration tests are harder to write and reason about — this is wasted effort when what you want to test is local. also, if a test fails, it’s difficult to tell where the failure occurred.
for example, consider a piece of code that does some physical calculation to set the throttle for a plane. only doing integration testing means that the physics calculation is never tested on its own, and so isn’t verified to be correct in all the required cases.
testing an e-commerce library
A programmer creates an e-commerce library, however the project has some bugs and other shortcomings. You can download a copy here.
- i imported the project into intellij and ran the unit tests that way.
- notes on the library:
PaymentDetails
is used to allow for future flexibility if something else needs to be added to the payment details- i’m not sure why
Order
exists. it potentially allows for more flexibility in the future, but it’s still highly coupled to theShoppingCart
class (it usesShoppingCart
’spaymentProcessor
). - the
ShoppingCart
contains almost everything — it’s a “god class”. for example, there’s no way to use theItem
class outside ofShoppingCart
, so displaying items in a GUI might be difficult - we might want
CardPaymentProcessor.charge
to throw an exception if the payment fails, rather than relying on return codes
- looking at the class for testing, we want to follow the “arrange act assert” paradigm. the
setup
method does this for us. the reason this is not done in a constructor is because the tests are being run by a testing library that would like to ensure the same preconditions for each test. we can get this by rerunning the setup method, rather than having to create a new instance of the class. - the second test doesn’t assert anything about what the output should be — the order could return an empty string and it would still pass. similarly, it doesn’t test whether the payment actually goes through. also, it tests multiple units of functionality — the
order.getNames()
method and theorder.chargePaymentCard()
method, which are logically different. - the first test doesn’t test adding an item to cart, so i added that.
- dependency injection is a class getting objects which it needs as inputs rather than creating them itself
- it abstracts away creation/initialization of objects, so that the user of the object doesn’t have to be changed when the implementation changes
- we can rewrite the
ShoppingCart
class, making theItem
andOrder
classes separate, and having theOrder.chargePaymentCard
method take aCardPaymentProcessor
as input.