50x Faster and 100x Happier: How Wix Reinvented Integration Testing
- Wix Engineering
- 1 hour ago
- 7 min read

Why Tests Are the Backbone of Healthy Projects
In the world of software development, testing is not just a formality; it's a lifeline. Tests act as our safety net, giving us the assurance that our code functions as intended, prevents regressions, and allows us to refactor and evolve the code confidently. However, not all tests are created equal, and the road is particularly rough when it comes to integration tests.
The Pain of Running Classic Integration Tests
Many developers rely heavily on unit tests to validate their code. While unit tests are great for checking individual components in isolation, they often fall short in providing the confidence needed for complex systems. The interaction between different application components can be validated through integration tests, uncovering issues that unit tests might miss
Docker-Based Integration Tests
Running integration tests within Docker containers takes this a step further. Docker provides a consistent, isolated environment that mirrors production, offering a reliable way to catch issues that might not surface when running code locally. Differences between development and production setups often lead to blind spots, but Docker bridges that gap effectively.
However, maintaining integration tests - especially in a Dockerized environment—can be a developer's worst nightmare.
Challenges with Docker-Based Integration Tests
Complex Setup: Setting up Docker environments to mimic production can be daunting. Configurations need to be precise, and managing these environments quickly becomes an overhead.
Flakiness: Docker-based tests often fail due to issues unrelated to the code, such as network glitches or container startup delays. This flakiness can severely disrupt development flow.
Slow Feedback Loop: Docker tests can be slow to initialize, leading to longer feedback cycles. This delays identifying and fixing issues, hampering development velocity and morale.
Given these pain points, many teams face a dilemma: unit tests don't provide enough confidence, but integration tests with Docker introduce too much friction.
The Wix Solution: In-memory Integration Testing
At Wix, we’ve tackled the integration testing challenges head-on by pioneering an approach that emphasizes In-Memory Integration Testing - a solution designed to increase speed, reliability, and developer productivity. By harnessing the principles of hexagonal architecture and automatic code generation of the app start-up and composition layers, tests are now at the forefront of development.
Our integration tests have gone from slow, multi-minute processes into lightning-fast executions that run in mere seconds.This ensures developers receive rapid feedback, enabling faster iterations and more polished code.
How Wix Streamlines Integration Testing
Hexagonal Architecture for Clarity and Decoupling
This design pattern separates business logic from external systems (e.g., databases, APIs) via ports and adapters. For testing, real dependencies are replaced with lightweight in-memory alternatives, maintaining fidelity while simplifying setup. This allows us to validate service behavior without unnecessary overhead.

Efficient Context Building with the App Context Builder
At Wix, the App Context is the complete environment that contains all the dependencies a service requires to run. The App Context Builder is responsible for constructing and wiring these dependencies, exposing only the service-specific setup to developers. This design ensures that core configurations - such as data layer or messaging layer setups—remain consistent across production and testing environments. As a result, the business logic stays untouched, while tests remain reliable, efficient, and easy to maintain.

In-Memory Testing for Lightning-Fast Feedback
Wix uses in-memory implementations for infrastructure components like databases and message queues, removing the need for Docker. This simplifies workflows, speeds up testing, and reduces errors. Critical contract integration tests validate in-memory replacements against Docker, written once at the infrastructure level to avoid redundancy across microservices.

Let’s Break It Down!
Hexagonal Architecture
Wix Dev Platform
Wix introduced Nile as an internal development platform designed to maximize productivity and developer satisfaction. With a focus on declarative code and smart code generation, Nile simplifies integrations, CRUD API implementation and internal event-driven processing and many other cross cutting concerns.
It removes much of the boilerplate typically required in microservices by offering out-of-the-box solutions for HTTP routing, input validation, authentication, authorization, and more. Major service components, such as the Data Access Object (DAO), are also included, providing easy configuration and an intuitive API for seamless data access.
Ports and Adapters
Nile organizes code using hexagonal architecture principles, ensuring business logic is cleanly separated from external systems. External collaborators - like the data access layer, RPC or HTTP clients, event handlers, and logging/metrics - are accessed through ports, which provide developer-friendly, business-focused interfaces.
Adapters act as bridges between these ports and their underlying integrations (e.g., MySQL, Kafka, RPC). This structure promotes a clean, modular design, streamlining testing and maintenance.

App Context Builder
Production Environment
Each Nile service’s production code is composed of two key parts:
The ControllerThe controller implements the protobuf interface (auto-generated in Java or Scala) that defines all the service’s APIs and events. It focuses exclusively on executing the business logic by leveraging the required collaborators or "ports" (e.g., data-layer, event producer) to fulfill business requirements. Any additional helper objects needed for business logic are constructed within the controller, ensuring that the controller encapsulates the entire business logic of the application.
The App Context BuilderThe App Context Builder plays a central role in assembling the controller's dependencies. It automatically builds collaborators (adapters) as part of the app context, which are then passed to the controller as interfaces (ports), maintaining a clean separation of concerns. Its strength lies in automating static building code, freeing developers to focus on dynamic configuration. This reduces repetition, boosts productivity, and streamlines service creation and testing for faster, cleaner development.
class ExampleApp {
private final DataAccessObject<Domain> dao;
ExampleApp(DataAccessObject<Domain> dao) {
this.dao = dao;
}
public Response createItem(CreateRequest request) {
// Business logic using dao collaborator
Domain domainObject = request.toDomain();
Domain createdObject = dao.insert(domainObject);
return new Response(createdObject.toDto());
}
public Response getItem(GetRequest request) {
// Fetch and map to DTO
Domain domainObject = dao.get(request.id);
return new Response(domainObject.toDto());
}Â Â
// Additional CRUD and business logic...
}
Controller - business logic only
public static ExampleApp create(ExampleAppContextBuilder contextBuilder) {
var context = contextBuilder.build(daoBuilder -> daoBuilder.withConfiguration());
return new ExampleApp(context.dao);
}Â
AppContextBuilder
Testing Environment
For testing, Nile provides a test-specific App Context Builder, which automatically generates and configures in-memory replacements for the production collaborators (e.g., data-layer, RPC-client). This leaves the production controller unchanged, as the intent of these in-memory integration tests is to validate the business logic across different components of the service working together seamlessly.
In-Memory Testing
A key feature of Wix-style integration testing is the use of out-of-the-box in-memory implementations for critical infrastructure components in the Nile backend platform. Core infrastructure components like the data layer, event publisher/subscriber, and RPC/HTTP clients are replaced with lightweight in-memory alternatives. Developers are expected to provide simple stub or mock implementations for their RPC or HTTP based collaborators.
This eliminates the need to run external Docker containers, such as MySQL, Kafka, or RPC/HTTP servers, making the testing environment faster, simpler, and more efficient.
In-Memory Data Layer
For example, in the data layer (DAO), the SQL layer is replaced with in-memory data structures provided by the infrastructure team to ensure consistent behavior with production. While this requires a significant upfront investment, the high usage across the organization makes it well worth the effort.
Most of the logic remains consistent between production and testing - including handling entity objects and performing validations, conversions, and encryptions. However, the storage implementation differs: the production MySQL layer generates JSON-based SQL statements, while the in-memory implementation uses an optimized data structure (e.g., a hash array-mapped trie) to store and retrieve DTO objects directly. This avoids the overhead of JSON serialization, leading to quicker execution while maintaining parity with production behavior.
Developers can trust this in-memory implementation, as it is rigorously tested against MySQL to ensure precise alignment with production, providing confidence in correct and consistent results.

A comparison between production and testing data layer variants
In-Memory RPC Client
The in-memory RPC client replaces live RPC servers during tests, offering a lightweight alternative. It allows developers to provide mock responses and simulate various scenarios, ensuring the business logic handles RPC interactions appropriately.
Thanks to the declarative nature of protobuf-based RPC endpoints, request validation (e.g. field lengths, email format) is handled automatically for the in-memory RPC client, just as it is for the production RPC client. This ensures consistent behavior when validating schema and payloads across both environments. The in-memory RPC client simplifies testing, eliminates external dependencies, mimics production responses, enabling services to be tested thoroughly with high fidelity.

A comparison between production and testing rpc clients variants
Benchmarks
In-memory integration tests have been introduced several years ago, since then close to 100% of new tests have been written in this style. The results have been remarkable, as the average execution time of in-memory integrations tests are (50!) times faster than legacy integrations tests:

Summary: In-Memory Integration Testing at Wix
Wix has redefined integration testing with its innovative In-Memory Integration Testing approach, replacing slow and unreliable Docker-based tests. By utilizing hexagonal architecture and the App Context Builder, Wix has transformed integration tests into rapid, reliable processes that execute in seconds - 50x faster than legacy methods. This lightning-fast feedback loop empowers developers to iterate quickly and maintain high-quality code.
The use of in-memory infrastructure components removes the need for Docker, simplifying workflows, cutting down errors, and boosting developer productivity. While maintaining in-memory test kits demands ongoing investment, the benefits at scale - enhanced velocity, reduced overhead, and better developer experience - far outweigh the effort.
Coupled with tools like the Nile platform and its declarative, boilerplate-free code generation, Wix’s approach propels development efficiency, making it a compelling model for robust, scalable software delivery in large organizations.

This post was written by Natan Silnitsky
You can follow him on Medium
More of Wix Engineering's updates and insights:Â
Join our Telegram channelÂ
Visit us on GitHubÂ
Subscribe to our YouTube channelÂ