I'm trying to learn unit tests using NUnit in .net and I must know to dispose somehow. I'm trying to implement in my worker class like Dispose Method and etc. I didn't want to implement IDisposable for my mock objects here because I know .Net Framework takes care of it for me.
This is de working class
public class ApplicationEvaluator: IDisposable
{
private const int minAge = 18;
private const int autoAcceptedYearsOfExperience = 10;
private List<string> TechStackList = new() { "C#", "RabbitMQ", "Docker", "Microservice", "VisualStudio"};
private IIdentityValidator _iIdentityValidator;
private bool disposedValue;
public ApplicationEvaluator(IIdentityValidator iIdentityValidator)
{
_iIdentityValidator = iIdentityValidator;
}
public ApplicatonResult Evaluate(JobApplication form)
{
if(form.Applicant == null)
{
throw new ArgumentNullException(nameof(form.Applicant));
}
if(form.Applicant.Age < minAge)
{
return ApplicatonResult.AutoReject;
}
_iIdentityValidator.ValidationMode = form.Applicant.Age > 50 ? ValidationMode.Detailed : ValidationMode.Qucik;
var validIdentity = _iIdentityValidator.IsValid(form.Applicant.IdNumber);
if(!validIdentity)
{
return ApplicatonResult.TransferredToHR;
}
var similarTechStackCount = GetSimilarTechStackCount(form.TechStackList);
if (similarTechStackCount < 25)
{
return ApplicatonResult.AutoReject;
}
if (similarTechStackCount > 75 &&
form.YearsOfExperience >= autoAcceptedYearsOfExperience)
{
return ApplicatonResult.AutoAccept;
}
if (_iIdentityValidator.CountryDataProvider.CountyData.Country != "TURKEY")
{
return ApplicatonResult.TransferredToCTO;
}
return ApplicatonResult.AutoAccept;
}
private int GetSimilarTechStackCount(List<string> List)
{
int count = List
.Where(x => TechStackList.Contains(x, StringComparer.OrdinalIgnoreCase))
.Count();
return (int)((double)(count / TechStackList.Count) * 100);
}
public enum ApplicatonResult
{
AutoReject,
AutoAccept,
TransferredToHR,
TransferredToLead,
TransferredToCTO,
}
// Default Dispose Pattern
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
_iIdentityValidator.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
// TODO: set large fields to null
disposedValue = true;
}
}
~ApplicationEvaluator()
{
Dispose(false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
Here is the class where I write my tests. I have created a method for the objects that I use all the time, and instead of newing the objects every time, I call the method. That's why using(//here is the object to delete) use only in initialise methods.
public class ApplicationEvaluatorTests
{
private Mock<IIdentityValidator> InitialiseTestMock()
{
var mock = new Mock<IIdentityValidator>();
mock.DefaultValue = DefaultValue.Mock;
mock.Setup(i => i.CountryDataProvider.CountyData.Country).Returns("TURKEY");
mock.Setup(i => i.IsValid(It.IsAny<string>())).Returns(true);
return mock;
}
private ApplicationEvaluator InitialiseTestEvaluator(Mock<IIdentityValidator> mock)
{
using (ApplicationEvaluator evaluator = new ApplicationEvaluator(mock.Object))
{
return evaluator;
}
}
private JobApplication InitialiseTestJobApplictaion()
{
using (JobApplication form = new JobApplication())
{
using(form.Applicant = new Applicant())
{
form.Applicant.Age = 18;
form.Applicant.IdNumber = "12345678910";
form.TechStackList = new List<string>() { "C#", "RabbitMQ", "Docker", "Microservice", "VisualStudio" };
};
return form;
}
}
// Yaş 18'den küçükse, AutoReject mi?
[Test]
public void Application_WithUnderAge_TransferredToAutoRejected()
{
// Arrange
var mock = InitialiseTestMock();
var evaluator = InitialiseTestEvaluator(mock);
var form = InitialiseTestJobApplictaion();
form.Applicant.Age = 17; // Test Case
// Act
var result = evaluator.Evaluate(form);
// Assert
// Assert.AreEqual(ApplicatonResult.AutoReject, result); Aşağıdaki ile aynı
result.Should().Be(ApplicatonResult.AutoReject);
}
.
.
.
(Test codes goes on)
}
Does what I'm doing make any sense? How can i improve my GC knowlage.
I would not recommend implementing IDisposable in unit tests instead rely on attributes provided by testing framework like OneTimeSetup, OneTimeTearDown, Setup, teardown to control the lifecycle of variables. This way we reduce the complexity in test code and also let the test runner's default behavior(which relies on these attributes) to control the life cycle. In the test code that you mentioned it would work even when another test is added. Its because of the using which is doing a proper cleanup. So my thumbs up for the code. If I were to add one more test then I would be even more lazy to just cleanup the variables in teardown.