---
title: Refactoring a test
description: Replace verbose arrange code with AutoFixture when a service test needs nested data and injected dependencies.
---

Real service tests often need a system under test (SUT) with injected dependencies and a valid domain object before the service accepts the call. This page starts with a hand-written example, then refactors it in two steps.

## Prerequisites

- .NET 8+ test project
- [AutoFixture](/docs/get-started/installation) in your test project
- [AutoFixture.AutoMoq](/docs/integrations/automoq) plus an explicit **Moq** reference — AutoMoq's own Moq dependency is kept low for compatibility, so add the latest Moq yourself for current features and fixes

## Example scenario

You are testing `ShippingLabelService.CreateLabel`. The service loads an order from a repository, validates the order is shippable, asks a rate calculator for total weight, and returns a multi-line label.

The test should verify the label includes the customer's name, destination country, and total weight in kilograms. You do not care about product price, billing zip codes, or most address fields — but the service still requires them to be populated.

## Example types

The service and its dependencies:

```csharp
public interface ICommerceOrderRepository
{
    Order? GetById(uint id);
}

public interface IShippingRateCalculator
{
    double CalculateTotalWeightKg(Order order);
}

public class ShippingRateCalculator : IShippingRateCalculator
{
    public double CalculateTotalWeightKg(Order order) =>
        order.OrderLines.Sum(line => line.Product.Weight * line.Quantity);
}

public class ShippingLabelService(
    ICommerceOrderRepository orders,
    IShippingRateCalculator rateCalculator)
{
    public string CreateLabel(uint orderId)
    {
        var order = orders.GetById(orderId)
            ?? throw new InvalidOperationException("Order not found.");

        // Validates line items, shipping address, billing address, then:
        var weightKg = rateCalculator.CalculateTotalWeightKg(order);
        var address = order.ShippingAddress;
        return $"{address.Name}\n{address.Street}\n{address.Zip} {address.Country}\nWeight: {weightKg:F1} kg";
    }
}
```

The order graph the service validates:

```csharp
public class Order
{
    public Order(uint id)
    {
        Id = id;
        OrderLines = new List<OrderLine>();
    }

    public uint Id { get; private set; }

    public IList<OrderLine> OrderLines { get; private set; }

    public ShippingAddress ShippingAddress { get; set; } = new();

    public ShippingAddress BillingAddress { get; set; } = new();
}

public class ShippingAddress
{
    public string Name { get; set; } = string.Empty;
    public string Street { get; set; } = string.Empty;
    public string Zip { get; set; } = string.Empty;
    public string Country { get; set; } = string.Empty;
}
```

`OrderLine` and `Product` are also part of the commerce model. The test pins product weight and line quantity because the weight assert depends on them; other product fields stay anonymous.

## The classic approach

A typical hand-written test wires the full graph and constructs the service manually:

```csharp
const uint orderId = 1001;

var product = new Product(501)
{
    Price = 24.99m,
    Weight = 1.25
};
var line = new OrderLine(product) { Quantity = 2 };

var order = new Order(orderId);
order.ShippingAddress = new ShippingAddress
{
    Name = "Jane Doe",
    Street = "742 Evergreen Terrace",
    Zip = "12345",
    Country = "Denmark"
};
order.BillingAddress = new ShippingAddress
{
    Name = "Jane Doe",
    Street = "742 Evergreen Terrace",
    Zip = "12345",
    Country = "Denmark"
};
order.OrderLines.Add(line);

var orderRepository = new Mock<ICommerceOrderRepository>();
orderRepository
    .Setup(r => r.GetById(orderId))
    .Returns(order);

var rateCalculator = new Mock<IShippingRateCalculator>();
rateCalculator
    .Setup(c => c.CalculateTotalWeightKg(order))
    .Returns(2.5);

var sut = new ShippingLabelService(
    orderRepository.Object,
    rateCalculator.Object);

var label = sut.CreateLabel(orderId);

Assert.Contains("Jane Doe", label);
Assert.Contains("Denmark", label);
Assert.Contains("2.5", label);
```

This works, but it has drawbacks:

- **Arrange dominates the test** — most lines exist only to satisfy validation, not to express what you are verifying
- **Repeated data** — shipping and billing addresses are duplicated even though the assertion only reads the shipping label
- **Incidental values** — product id, price, and street names add noise when the test cares about name, country, and weight on the label
- **Manual SUT wiring** — `new ShippingLabelService(...)` repeats constructor parameters you will repeat in every test for this service

## Step 1 — refactor the service dependencies

Install [AutoFixture.AutoMoq](/docs/integrations/automoq) and an explicit latest **Moq** reference if you have not already (do not rely only on AutoMoq's low transitive Moq dependency), then customize the fixture so AutoFixture creates the service without `new ShippingLabelService(...)`. Mock only collaborators that cross a process boundary.

`ICommerceOrderRepository` loads data from storage — mock it. `ShippingRateCalculator` only sums order line weights in memory; it does not call databases, HTTP APIs, or the file system. In a **classicist** unit test, keep that kind of collaborator real and mock I/O at the edge.

```csharp
const uint orderId = 1001;

// ... build product, order, addresses, and order lines by hand ...

var fixture = new Fixture().Customize(new AutoMoqCustomization());

var orderRepository = fixture.Freeze<Mock<ICommerceOrderRepository>>();
orderRepository
    .Setup(r => r.GetById(orderId))
    .Returns(order);

var rateCalculator = fixture.Freeze<ShippingRateCalculator>();
fixture.Inject<IShippingRateCalculator>(rateCalculator);

var sut = fixture.Create<ShippingLabelService>();

var label = sut.CreateLabel(orderId);

// ... Assert.Contains for name, country, and "2.5" ...
```

The order graph is still built by hand, but you drop manual SUT construction and the rate-calculator mock setup. AutoMoq supplies the repository mock; `Freeze` + `Inject` register the real calculator as `IShippingRateCalculator`.

**What improved:** less constructor noise; weight on the label now comes from real calculator logic instead of a hard-coded mock return value.

**What is still painful:** the order, addresses, product, and repository `.Setup` block are unchanged.

## Step 2 — refactor the test data

Generate anonymous specimens where values do not matter. Use `Build` only for fields the test or service rules require:

```csharp
var fixture = new Fixture().Customize(new AutoMoqCustomization());

var orderRepository = fixture.Freeze<Mock<ICommerceOrderRepository>>();
var rateCalculator = fixture.Freeze<ShippingRateCalculator>();
fixture.Inject<IShippingRateCalculator>(rateCalculator);

var shippingAddress = fixture.Build<ShippingAddress>()
    .With(a => a.Name, "Jane Doe")
    .With(a => a.Country, "Denmark")
    .Create();

var product = fixture.Build<Product>()
    .With(p => p.Weight, 1.25)
    .Create();

var line = fixture.Build<OrderLine>()
    .FromFactory(() => new OrderLine(product))
    .With(l => l.Quantity, 2u)
    .Create();

var order = fixture.Build<Order>()
    .Do(o => o.OrderLines.Add(line))
    .With(o => o.ShippingAddress, shippingAddress)
    .With(o => o.BillingAddress, shippingAddress)
    .Create();

orderRepository.Setup(r => r.GetById(order.Id)).Returns(order);

var sut = fixture.Create<ShippingLabelService>();
var label = sut.CreateLabel(order.Id);

Assert.Contains("Jane Doe", label);
Assert.Contains("Denmark", label);
Assert.Contains("2.5", label);
```

Pin values the assertion depends on (`Weight` and `Quantity` → `2.5` kg). Do not re-call `CalculateTotalWeightKg` in the assert — that only proves the SUT and the assert used the same collaborator. Assert a known expected result from known inputs instead.

**What improved:**

- **Pin only what matters** — name and country for the destination; weight and quantity for the kg line on the label
- **Reuse one address** — billing and shipping share the same instance instead of duplicating five fields
- **Anonymous filler** — order id, street, zip, and product id come from AutoFixture
- **Known expected value** — `Assert.Contains("2.5", label)` matches the classic example

## How it works

- `AutoMoqCustomization` — creates Moq mocks for interface constructor parameters you do not replace
- `Freeze<Mock<T>>()` — one shared mock instance for setup and verification
- `Freeze<T>()` + `Inject<I>()` — freeze a concrete type and supply it wherever the interface is requested
- `Create<T>()` — anonymous values for properties the test does not care about
- `Build<T>()` + `With` / `Do` / `FromFactory` — pin properties, add items to read-only collections, or control construction
- `FromFactory` — required when a type needs a constructor argument you already built (here, `OrderLine(Product)`); AutoFixture cannot invent that graph from `Create<OrderLine>()` alone if you need a specific product instance

Plain `Create<Order>()` still fails here because the service validates line items, addresses, and weight. `Build` lets you satisfy those rules without a fully manual graph.

## Next steps

- [Introduction](/docs/get-started/introduction)
- [Integrations overview](/docs/integrations)
- [AutoMoq](/docs/integrations/automoq)
- [Build DSL](/docs/fundamentals/build-dsl)
- [Register, Freeze, and Inject](/docs/how-to/register-freeze-inject)
