What is code pollution and how to fix it - A dependency injection lesson
Tags: programming, beginners, testing, csharp
Published: 2024-06-22 15:31:23

## What is code pollution?

_Code pollution_ is an antipattern that takes place when you introduce additional code to your production code base to enable unit testing. [1]

This antipattern appears in two forms:
1. As a new method only called from the unit tests.
2. As a flag that changes the behavior of a class to indicate that it is called from a unit test.

Remember that _code is a liability, not an asset_. Adding code to production for the sole purpose of unit testing decreases the project's maintainability.

## Examples of code pollution

Case 1: Adding a method that is only called from the unit tests.

```csharp
public class MyClass
{
	private readonly IConfiguration _configuration;

	public MyClass(IConfiguration configuration) 
	{
		_configuration = configuration;
	}

	public string Run() 
	{
		if (GetConfigurationValue() == "FooBar")
		{
			return "Do cool stuff";
		}
		return string.Empty;
	}

	// Only used for testing
	internal string GetConfigurationValue()
	{
		return _configuration["ConfigurationValue"];
	}
}

[TestClass]
public class MyTest()
{
	[TestMethod]
	public void TestConfigurationValue()
	{
		string configurationValue = "FooBar";
		
		IConfiguration configuration = new ConfigurationBuilder()
		    .AddInMemoryCollection(
			    new Dictionary<string, string>() { "ConfigurationValue", configurationValue })
		    .Build();
		
		MyClass myClass = new MyClass(configuration);
		
		Assert.AreEqual(configurationValue, myClass.GetConfigurationValue());
	}
}
```

Case 2: Injecting a flag to change the class or method behavior.

```csharp
public class MyClass
{
	private readonly IConfiguration _configuration;
	private bool _isTestEnvironment;

	public MyClass() {}

	internal MyClass(IConfiguration configuration, bool isTestEnvironment)
	{
		_configuration = configuration;
		_isTestEnvironment = isTestEnvironment;
	}

	public void Run(string createValue, string executeValue)
	{
		Repository myRepository;
		
		if (_isTestEnvironment)
		{
			// Stuff for test environment
			myRepository = CreateTestRepository(createValue);
		}
		else
		{
			// Stuff for normal environment
			myRepository = CreateRepository(_configuration, createValue);
		}

		myRepository.DoStuff(executeValue);
	}
}
```

## How to fix code pollution

Case 1. Fixing method or property pollution.

If you are exposing private properties or methods, you are exposing implementation details. In that case, you should change your unit test to actually test the system's behavior.

```csharp
[TestClass]
public class MyTest()
{
	[TestMethod]
	public void TestConfigurationValue()
	{
		string configurationValue = "FooBar";
		
		IConfiguration configuration = new ConfigurationBuilder()
		    .AddInMemoryCollection(
			    new Dictionary<string, string>() { "ConfigurationValue", configurationValue })
		    .Build();
		
		MyClass myClass = new MyClass(configuration);
		
		Assert.AreEqual("Do cool stuff", myClass.Run());
	}
}
```

Case 2. Fixing injection pollution.

If you need to inject a parameter or configuration to test the system, you are not applying dependency injection properly.

In our example, we use a `bool` flag to indicate our program to create a test `Repository` instead of the normal one to avoid using production dependencies `IConfiguration`.

Instead, we should inject `Repository` as a method dependency, or inject a `RepositoryBuilder` as a constructor dependency.

```csharp
// Method injection
public class MyClass
{
	public MyClass() {}

	public void Run(Repository repository, string executeValue)
	{
		repository.DoStuff(executeValue);
	}
}

// Constructor injection
public class MyClass
{
	private readonly RepositoryBuilder _repositoryBuilder;
	
	public MyClass(RepositoryBuilder repositoryBuilder) 
	{
		_repositoryBuilder = repositoryBuilder;
	}

	public void Run(string executeValue)
	{
		_respositoryBuilder.Build().DoStuff(executeValue);
	}
}
```

This way, we can inject the testing repo from our unit test, and the production repo in the production code base.

## References 

1. Khorikov, V. (2020). _Unit testing principles, practices, and patterns_. Manning Publications Company.