Option 1: using a seam
The easiest way is to reorganize this method using a seam:
public void ExecuteSomeCommand() { this.CreateCommand(someInt, SomeEnum.EnumValue).Execute(); }
This way you can intercept the creation of the βnewβ statement by extending this class. When done manually, it might look like this:
public FakeSomeService : SomeService { public int SomeInt; public SomeEnum SomeEnum; protected override Command CreateCommand(int someInt, SomeEnum someEnum) { this.SomeInt = someInt; this.SomeEnum = someEnum; return new FakeCommand(); } private sealed class FakeCommand : Command { public override void Execute() { } } }
This fake class can be used in your testing methods.
Option 2: Separate Behavior and Data
The best way is to separate the data from the behavior. The team has both data (message) and behavior (processing this message). If you are allowed to make such a change in the code base: separate it, for example, by defining commands and command handlers. Here is an example:
// Define an interface for handling commands public interface IHandler<TCommand> { void Handle(TCommand command); } // Define your specific command public class MyCommand { public int SomeInt; public SomeEnum SomeEnum; } // Define your handler for that command public class MyCommandHandler : IHandler<MyCommand> { public void Handle(MyCommand command) { // here your old execute logic } }
Now you can use dependency injection to inject the handler into the class you want to test. Now this class will look like this:
public class SomeService { private readonly IHandler<MyCommand> handler;
Since you have now separated the data from the behavior, it will be very easy to create a fake command handler (or create it using Rhino mocks), which checks if the command is sent correctly to the handler. In manual mode, it will look like this:
public class FakeHandler<TCommand> : IHandler<TCommand> { public TCommand HandledCommand { get; set; } public void Handle(TCommand command) { this.HandledCommand = command; } }
This fake handler can be reused during the unit testing project. A test using this FakeHandler might look like this:
[TestMethod] public void SomeTestMethod() {
Disabling data from behavior not only makes your application more testable. This makes your application more resilient to change. For example, cross-cutting issues can be added to execute commands without the need to make changes to any handler in the system. Since IHandler<T> is a one-method interface, it is very easy to write a decorator that can wrap each handler and add things like a log, audit audit, profiling, validation, transaction processing, fault tolerance, etc. You can learn more about this in this article .