Krzysztof Pich

Code 8779051 1280

Unit Tests – start with architecture instead of detail

Implementing unit tests allows you to begin with the solution goal and work your way through an object’s composition down to the details. When working on a project without unit tests, you naturally start from the current codebase and build towards the solution. This approach is initially straightforward but can lead to leaving the most challenging decisions for the end of the task when the majority of the code is “ready.” This can significantly impact keeping the task on budget and on schedule.

When you start with unit tests, you gain a powerful new tool. You can execute code without running the entire application or requiring a starting point in the current codebase.

Example

Imagine you need to send a payload after order execution to an ERP system, a common feature in e-commerce systems. Here are some requirements for our example ERP:

  • Communication utilizes JSON payloads.
  • Authorization is based on a bearer token.
  • Each request uses a couple of fields from the payload plus a secret to generate a hash – the algorithm is described in the documentation.
  • The order payload requires customer data, an item list, and a shipping code.

Standard approach without unit tests

When coding in PHP, the natural thought process would be to start by searching for the required payload data in the current system. Once complete, you would proceed to implement a parser to serialize the JSON, then build a class that takes the payload and constructs a request with authorization. What are the consequences of this approach?

  • If you haven’t considered how authorization and payload errors are handled by the sender class, you’ll need to adapt the already created code to the chosen approach – typically when the task is almost finished.
  • Questions about your system, like “How to get an order item with regular price and discounted price?” are easy to solve – you have code to examine, and you’re an expert in your system. However, questions like “Is the ‘number’ discount in the documentation a percentage or an amount?” can be time-consuming due to communication delays between you and the ERP team.
  • You’re unable to test the solution without having almost everything implemented – if it turns out that the documentation is outdated for a few items, you’ve just spent a week on the solution, the sprint is about to end, and you need answers from the client or ERP team.

Start with unit tests – how it will help

You’ll begin with the ErpSender class interface and dependencies. It’s time to consider the following questions and observations:

  • I need a send() method that will take a string and build authorization for the system.
    • I can validate the string with the json_validate method.
    • Where should I build the hash in the payload? I should consider this when building the payload builder.
  • The bearer token can be provided by ErpConfiguration class
  • For error handling, I should throw an exception. I’ll need to stop order processing, store it for retry, or mark the order. Consider where to log errors and whom to notify – if any of this isn’t specified at the beginning of the task, you have excellent questions for the business team.

Assume we have the ErpSender class ready, knowing it will throw an Exception on error, return void, and need a payload JSON string from the invoking class – everything else is handled by dependency injection. What we need to build is the PayloadBuilder class. Now we should consider:

  • How to build the hash? Hashing isn’t part of payload building but will need a specific list of fields for each payload. The hash will need the ErpConfiguration class as a dependency to get the secret and payload data.
  • This is the best time to ask architectural questions:
    • Will the ERP be used only to process orders, or will other data be sent from e-commerce, like customers, items, etc.? At this point, you might want to reach out to the PM, Solution Architect, or business directly.
    • If yes – it might be beneficial to implement a design pattern like composite, builder, or factory method to solve the hashing problem.
  • Do I need to store payloads, and do I need a retry process?
  • Maybe I need an OrderProcessor class, and PayloadBuilder will be used in that class to fulfill single responsibility?
  • How should I build the payload – utilizing e-commerce system classes and interfaces, or do I need to create Data Access Objects for ERP due to differences or complexity of data in the system?

After this process and creating necessary tests and implementations, you can delve into your e-commerce system details. This should be the 3rd or 4th day of work, and you’ve answered all architectural and the majority of functional questions. You can focus on the level where you have the most experience, answering questions like:

  • What should be the place to get order data?
  • How to collect all data?
  • How to handle different product types?

These kinds of questions rarely rely on business or external teams – you can solve them by checking e-commerce platform documentation or simply reading the code. It’s wise to leave that for the end instead of focusing on it at the beginning of the journey.

Summary

When working with unit tests and starting from the end, you’ll be able to think about missing technical details and architectural design earlier and with a smaller codebase that will require adaptation to decisions. This can provide you with necessary time for communication and establish you as a trustworthy professional.