C# standardizing tests - Part 2
Best Practices
Regions
Within test classes and generally, in all classes, I have always liked to divide sections or responsibilities into regions. So you will probably see some of the following most of the time:
Attributes: for writing read-only properties.
Properties: to write properties that can be public but also private.
Constructor Methods: write all the constructor methods.
Public Methods: to write all the public methods.
Private Methods: to write all the private and protected methods.
The test class is created by inheriting the ClassFixture and defining a private read-only variable called _fixture to which the fixture service instance will be injected by the class constructor.
public class MetadataServiceTests : IClassFixture<MetadataServiceFixture>
{
#region Attributes
private readonly MetadataServiceFixture _fixture;
#endregion
#region Constructor Method
public MetadataServiceTests(MetadataServiceFixture fixture)
{
this.fixture = fixture;
}
#endregion
...
Naming the test
When running the tests from a pipeline or the test explorer, the observed name can summarize in an agile way what the test was doing and to which service it corresponds.
To achieve this, we can create a nomenclature for each test as follows:
Class tested_Method tested_Values set_Expected result.
For example:
[Fact]
public void MetadataService_GenerateMetadata_ParametersOk_ReturnOk()
{
...
}
Triple "A"
In the creation of the test, we give aesthetics to the definition of the test by organizing the sections into three areas:
Arrange: to define or build the objects to be used.
Action: to invoke or execute the actions to be tested.
Assert: to carry out all the desired checks.
[Fact]
public void MetadataService_GenerateMetadata_ParametersOk_ReturnOk()
{
//Arrange
//Action
//Assert
}
It may happen that sometimes we only need one of the three or a combination of them. It is only necessary to define them together or the one we need.
For example:
[Fact]
public void MetadataService_GenerateMetadata_ParametersNull_ReturnException()
{
//Arrange
_fixture.SetService(null);
//Action Assert
Assert.Throws<ArgumentNullException>(() => _fixture.GetSut());
}
Protect class services in your constructors
It is not always possible to control what we are receiving through the constructor of our class, but as a general rule, we should protect our classes by validating that what is arriving as a parameter in the constructor is defined and is not a null value that can affect the internal logic of our class.
It is common to find services to test that contain N number of Services injected in the constructor, so at least this allows us to see when we have not performed the configuration of that new service that has been passed as a parameter.
To protect the classes it is enough to make this validation in our class:
public class MetadataService : IMetadata
{
#region Attributes
private readonly IUtilities _utilities;
private readonly IUrlUtils _urlUtils;
private readonly IQRImageBuilder _qrBuilder;
#endregion
#region Constructor Methods
public MetadataService(IUtilities utilitiesService,
IUrlUtils urlService,
IQRImageBuilder qrService)
{
_utilities = utilitiesService ?? throw new ArgumentNullException(nameof(utilitiesService));
_urlUtils = urlService ?? throw new ArgumentNullException(nameof(urlService ));
_qrBuilder = qrService ?? throw new ArgumentNullException(nameof(qrService ));
}
#endregion
...
We will start with the implementation of the fixture service which is injected in the test. See you in part 3...
Subscribe to my newsletter
Read articles from Jaime Andrés Quiceno González directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Jaime Andrés Quiceno González
Jaime Andrés Quiceno González
I am motivated Software and Telecommunications Engineer with 9 years of experience in analysis, design, implementation, testing and deployment of software solutions. I am passionate about knowing the context of the problem in detail to end an adequate solution applying SOLID principles and design patterns to deliver software products with quality and efficiency. I have supported and innovated with automation various business and industrial processes through several programming languages and work frameworks with and without Agile methodologies. I have skills and knowledge in both FrontEnd and BackEnd.