I am building a web application in php. I am following TDD (write tests before production code) for my unit tests and using BDD to drive discovery of my applications features and to provide integration tests.
My architecture is based around Robert Martins Clean Architecture where by I have application use case classes / interactors that form the primary classes of my application.
My BDD steps call the application use case classes directly to drive the application. TDD tests are written before any new line of code and provides unit tests for all classes. With these two development methods, I have full test coverage of my application for integration and unit tests.
The problem I have run into is that whilst I have tested everything in the application, and confirmed all required features have been implemented, what I don’t have is a nice Web Application UI which a user can use. Currently the only users who can use the application are developers who would need to call the application use case classes directly. For developers, the code base is 100% covered by unit tests (TDD) and integration tests (BDD).
What I need to do now is create a browser application that will call URIs. These URIs will be linked to the application use cases. Like any modern day browser application, the browser application will call multiple URIs asynchronously to provide a rich user experience.
My question is: How do I test this part of my code base? This isn’t a question about which tool to use (although recommendations would be welcomed) but more at what level should I test? I guess this question could also be written as
‘I am a front end developer creating a front end web app using a library or web service that has been created by another developer. How should I test my app?’
My two thoughts are:
- Test the browser application and associated URIs but mock out the application use cases that are called by the controllers
- Leave the controllers call the real application use cases and in effect provide another level of integration tests / end to end tests with the addition of the web delivery layer (browser application and URIs)
The problem I see with 1) (as with any tests where mocks are involved) is that if I incorrectly mock the requests and responses for that application use cases, there will be no tests to pick this up and bugs will pop up once everything is integrated together.
This then leads to the obvious answer that we should test with more integration using 2). The issue I have with this is that I feel I will now be duplicating a lot of integration tests and testing will be very slow.
Im also not sure what level of tests I should write. Should I write large, user acceptance test that take a number of different use cases combined together? e.g. test that a product can be added to a store, purchased by a customer and then shipped. Or should I test each application UI view on its own? e.g. customer can view product for purchase.
All the testing scenarios you scetched make sense, but which is best suited for your case depends, well, on the details of your case. As a rule of thumb, if you have two complex components A and B, you should have a lot of tests to test each of them in isolation, and at least some integration tests as well. If component A or B is “not too complex”, you might omit the isolated tests and use integration tests to cover both in one, as long as this satisfies you.
Since you do not have a Web UI currently, you might consider to create your tests “as you go” and try out what works best for you. For example, when your system allows easily to create or recreate a test environment with the real backend, and running automated tests using that backend is fast enough, then use that approach. If, however, you notice that the tests are getting too complicated that way, you have problems to manage the test data with the real application, or you notice (not just suspect) testing becomes very slow, or some other kind of problems, then you will probably be better off with a mocked backend.
Another factor is how many changes you expect in which area. If the backend and its API is very stable, it will probably more efficient to focus on isolated UI tests. If the backend changes often, UI/backend integration tests get more important. If the UI is stable, you can probably automate lots of tests (with a tool like Selenium). If the UI is heavily under development, automation will probably bring you less benefit since “user experience” and testing ergonomic aspects will be more important, and that is nothing which you can automate.
So I would expect you will probably need all kind of tests, but if it will be 10% tests of type A and 90% tests of type B, or vice versa, is something you can only find out by yourself.
I will assume the back-end API is tested and ready to go and all you need to worry about now is a nice UI.
If you’re writing this as a web UI, then Selenium is your tool of choice. What this does is allow you to replay actions that a user performs. So you write the UI, then use it in various test cases by clicking buttons and whatnot, and then you can reply the exact same set of steps using Selenium. You now have a test runner.
Whether you unit test your front end by replacing the back-end with mocks or just plug it all together and integration test it all is up to you – I think the cost of creating unit tests here is prohibitive, better to go straight for integration tests and unit test the back-end components.
You’re going to have to create the integration tests anyway, and unit testing the front end can be slow anyway (all the setup of the web server and test tools) and the cost of mocking can be very high – it might be easy to mock a simple method, but with UIs you’re often triggering some more complex task (eg the ‘pay now’ button calls the ‘DoPay’ API – that’s either too trivial to unit test, or too complex to unit test depending whether you want some results from calling DoPay or not), or retrieving data for display.
You’ve unit tested the back-end APIs in isolation so putting the UI on is all about working those APIs together.
It’s very hard to test the UI portion except via a browser, and it’s hard to automate the browser side of things. Testing at this level often falls back on human testers because of this.
By necessity it will duplicate much of the integration testing (ideally it should duplicate all of it, the integration testing is supposed to test all the use cases the end user will be following as they use the site). Start by writing the acceptance tests to cover the high-level workflows users will follow which will combine several integration tests, eg. add products to the cart, remove some, add more, check out and ship. Combine that with view-by-view tests of basic functionality, eg. a test involving pulling up one product and going through all the things a user could do on the product page (asking for specs, for example).