ciarlill's avatar

A general (long) question on TDD and test organization

So lets say we have a resource called 'Organization'. The application has 3 basic roles: admin, manager, staff. The roles are not really important, just showing that we need a policy for who can change what in the Organization. We have the following classes that support working with an organization via an API:

  • Organization (model)
  • OrganizationController
  • OrganizationPolicy
  • OrganizationRepository
  • OrganizationValidator
  • OrganizationService (maybe)

Writing up unit tests for the Policy and Validator are fairly straightforward, but it brings me to my first question:

1. When testing policy's and validation rules, do you test every possible failure case along with every possible success case? If Organization has some ingrained business logic in regards to what fields can be updated by what roles, it would seem that this could get unwieldy fast. Same goes with complex validation requirements. Do you test every possible way validation could fail, ensuring it does?

Next lets look at the repository. The repository relies on the validator which we already tested.

2. Do we still test that the repository uses the proper validation logic for all possible cases? Or do we just trust our validation tests and make sure the repository is creating records in the DB?

My hunch is that we just mock the validator and make sure the proper validation method is called for each repository method.

Which brings us to the controller. This seems like the ideal dividing line for unit/functional testing. I am feeling that controller tests should really be "integration" or "functional" tests (depending on how you label them). Only mock what you absolutely need to. But it still brings me to the question:

3. How many possible failure states should we test at the "integration/functional" level? Both when it comes to the policy and the validation?

When you start combining all the possible ways to test failure cases, it can feel like its growing exponentially. Do we test every role? Do we test every possible combination of fields for validation?

Lastly would be the service if one is needed. It most likely has dependencies on one or more repositories and other services.

4. Do we just mock the services dependencies and cover the service with unit tests? Or should this be part of our integration test layer as well?

So I guess what I am trying to get at is: how can the typical laravel application structure inform our layers of testing? I know hard and fast rules are rarely appropriate, but I feel like a good set of guidelines would make cleaning up our tests and introducing new devs to testing much easier.

Maybe something like this:

Unit: Policy, Validation, Repository

Integration: Services

Functional: Controllers

Am I making any sense here? Thanks.

tl;dr: 1) How to organize levels of testing based on common laravel application structure and classes? 2) How deeply do you test failure cases for validation and policies?

0 likes
2 replies
shez1983's avatar

ong post.. I will try to answer it my way (and then you can ask q if it doesnt satisfy you) If a controller is using Request/Validation then yes I would test to make sure validation passes/fails as appropriate for main things ie user tries to register an email thats already been registered or your own custom password rules.. (no point in testing other stuff as thats laravel built in and for sure would have been tested by that) generally speaking if you are creating more than one tests for a controller, if you haev already tested something in one controller no need to test/assert it again.. you also mock stuff like services or repository in a controller and you have dedicated repositoryTest or ServiceTest you should also test any methods you create in Models.

michapietsch's avatar

Came here looking for advice on test organization. It can be tough in complex applications! You're right, on unit level it's quite straightforward but if you're testing integration or E2E then the number of combinations can be overwhelming!

Let's pick out validation as an example:

First, I think testing on unit level can ensure that request does not get processed further if it's not valid. But I'd go as far as to test on browser level. Because user experience matters. E.g. does the user get appropriate feedback? I'm a huge fan of browser testing and I'm glad there are great tools to make it easy. It's just my preferred way.

I'd write a test for every aspect you need to cover in development. Even if you're at first just writing down the test definitions it's good documentation for stakeholders. :) Because a lot of work happens in the shadows. The customer will mostly only test the usual path without all the side roads you need to consider and cover and of course you test all of them manually during development. So writing more tests and covering all potential edge cases is a great way of forcing yourself to think about and cover all of them and to make the development process comprehensible to others.

Every single validation rule is a piece of work that needs to be done, tested and continue to work correctly in the future.

Please or to participate in this conversation.