@theusernamehasalreadybeentaken It depends what you actually want to test, I suppose. For things like service classes, I usually create “integration” tests where I can create the models using factories, call methods on the service class, and then assert that the expected records were created/updated/deleted.
Mar 20, 2024
3
Level 1
Best approach for testable service layer
We're starting a project that's going to be really complex with a lot of routes and business logic. We don't want to use domain abstractions because nobody has experience using it, the risk is too high and the learning curve is steep.
Is this a bad approach?
We need to add some extra checks in some methods that are just safe checks but aren't part of the validation, or can be called without validation from some sources. It's really an exceptional situation thing to catch.
The tests would have to simulate the model state.
class ProductService
{
public function find(int $productId): ?Product
{
return Product::find($productId);
}
public function create(array $product): Product
{
return Product::create($product);
}
// you'll have to find the product before calling this method but if you already have it avoids a unnecessary query
public function createTags(Product $product, array $tags): Product
{
return $product->tags()->createMany([
['name' => 'A new tag.'],
['name' => 'Another new tag.'],
]);
}
public function associateReference(Product $product, Reference $reference)
{
if ($reference->isExpired) { throw new ExpiredReferenceException(); }
return $product->reference()->associate($reference);
}
public function calculate(Product $product, int $number)
{
$result = 'example';
// < some really complex calculations here >
// it uses $product data directly
// so the unit test would have to simulate the model state
return $result;
}
Feel free to share some code on how you structure all this and how you call it on Controller, Jobs, Commands.
Please or to participate in this conversation.