Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions docs/integration/moq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,7 @@ You can configure the automatic mocks and/or assert calls on them as you would n
Configuring Specific Dependencies
=================================

You can configure the ``AutoMock`` to provide a specific instance for a given service type (or apply any other registration behavior),
by using the ``beforeBuild`` callback argument to ``GetLoose``, ``GetStrict`` or ``GetFromRepository``, in a similar manner
to configuring a new Lifetime Scope:
You can configure the ``AutoMock`` to provide a specific instance for a given service type (or apply any other registration behavior), by using the ``beforeBuild`` callback argument to ``GetLoose``, ``GetStrict`` or ``GetFromRepository``, in a similar manner to configuring a new Lifetime Scope:

.. sourcecode:: csharp

Expand All @@ -111,8 +109,7 @@ to configuring a new Lifetime Scope:
}
}

The ``cfg`` argument passed to your callback is a regular Autofac ``ContainerBuilder`` instance, so you can
do any of the registration behavior you're used to in a normal set up.
The ``cfg`` argument passed to your callback is a regular Autofac ``ContainerBuilder`` instance, so you can do any of the registration behavior you're used to in a normal set up.

You can also configure the ``AutoMock`` to use any existing mock, through the ``RegisterMock`` extension method:

Expand All @@ -133,3 +130,33 @@ You can also configure the ``AutoMock`` to use any existing mock, through the ``
// ...and the rest of the test
}
}

Mixing Real Framework Services with Auto-Mocking
================================================

Be careful when adding real framework services - such as Entity Framework Core or ``Microsoft.Extensions.Logging`` - into the ``AutoMock`` container.

``AutoMock`` automatically supplies a mock for any service it cannot otherwise resolve. That includes the individual elements of an otherwise-empty collection dependency. For example, a framework that depends on ``IEnumerable<ILoggerProvider>`` would normally receive an *empty* collection when no providers are registered. Under ``AutoMock``, the empty collection instead gets a single mock element injected into it.

A loosely-mocked element returns ``null`` from any member that hasn't been explicitly set up. When a framework consumes that collection and assumes the members return non-null values, this can surface as a confusing failure deep inside the framework's own code - frequently a ``NullReferenceException`` that has nothing obviously to do with your test.

A common example is constructing an Entity Framework Core ``DbContext`` inside an ``AutoMock`` container that also has logging wired up. EF Core asks for the registered logger providers, receives the mock element whose ``CreateLogger`` returns ``null``, and then throws when it tries to use that logger.

.. note::

You may see this behavior appear intermittently - for instance, only when tests run in a particular order. That is usually because the framework caches internal state (EF Core, for example, caches an internal service provider), which can mask the problem when a "good" code path runs first. The underlying cause is the same regardless of ordering.

If you need real framework services in your test, register concrete implementations for the services that framework consumes so ``AutoMock`` does not fill those slots with mocks. For example, provide a real logger factory (``NullLoggerFactory`` lives in the ``Microsoft.Extensions.Logging.Abstractions`` namespace):

.. sourcecode:: csharp

using Microsoft.Extensions.Logging.Abstractions;

using (var mock = AutoMock.GetLoose(cfg =>
cfg.RegisterInstance(NullLoggerFactory.Instance).As<ILoggerFactory>()))
{
// Real logging is used instead of a mock, so frameworks that consume
// the logger providers behave as they would outside of AutoMock.
}

Alternatively, avoid mixing real framework wiring into the auto-mocking container - keep the auto-mock container focused on the system under test and its direct dependencies.