Moq kütüphanesinin Mock sınıfları oluşturulurken varsayılan davranış şekli Loose'dur. Bu şekilde tanımlanan mock nesneleri metot çağrıları için kurulum zorunluluğu dayatmaz.
Çalıştığı doğrulanmak istenen metot kurulumu için Verifiable() olarak işaretlenir.
Metotlarının kurulumları yapılan mock nesne Assert bölümünde Verify() metodu çağrısı ile doğrulanmalıdır.
Metotların kurulumunda Verifiable çağrısı yapılmadan, mock nesnenin tüm çağrılarının (Verifiable veya değil) doğrulamasını sağlamak için VerifyAll() metodu kullanılır. (Önerilen pratik budur)
2. Çalışmaması gereken metot çağrılarını tespit eder.
Çağrılan her metodun setup'ı yapılmalıdır.
Setup'ı yapılmayan bir metot çağrıldığında aşağıdaki hata alınır:
3. Assert bölümlerinde Verify işlemleri sadeleşir
Verify edilmesi gereken metotların kurulumları da Arrange bölümünde yapıldığından Assert bölümün daha sade ve anlaşılır hale gelir.
4. Metodun fazla sorumluluğu varsa bunu önplana çıkarır
Doğrulanması gereken çağrılar için setup gerektiğinden, setup'ın ve test edilen metodun büyüklüğü gözle görülür hale gelir.
Büyük bir test kurulum bölümü, test edilen metodun gerektiğinden fazla sorumluluğa sahip olduğunun ve yeterince soyutlama yapılmadığının göstergesidir.
Extract Class veya Move Method yöntemleri uygulanabilir.
Uygulama Örneği
TestFixture sınıfları Foo.Framework.UnitTests v2.0 nuget paketinde bulunan BaseTestFixture sınıfından türetilir.
BaseTextFixture sınıfı
Örnek TestFixture tanımı
Testlerde kullanılan mock member bileşenleri aynı pakette bulunan StrictMock tipinde tanımlanır.
Örnek Setup metodu
BaseTestFixture sınıfı her testten sonra çalışmak üzere TearDown metodunu içerir ve bu metotta VerifyMocks metodu çağrılır.
BaseTestFixture sınıfında bulunan abstract VerifyMocks metodu implemente edilir ve TestFixture'da bulunan tüm mock memberlar için VerifyAll metodu çağrılır.
Bu metot bir mock için yapılan setup işlemi Verifable olarak işaretlenip işaretlenmeme durumuna bakmaksızın mock nesne için Verify işlemini uygular.
Örnek VerifyMocks implementasyonu
Testlerde Arrange bölümünde setup işlemlerini Verifiable olarak işaretlemek ve Assert bölümünde mocklar için Verify veya VerifyAll çağrısı yapmaya gerek yoktur.
Örnek Test
Mock member içermeyen TestFixture sınıfları
Mock member içermeyen TestFixture sınıflarında mock Verify işlemine gerek olmadığından bu sınıflar BaseTestFixture yerine LooseBaseTestFixture sınıfından türetilirler.
Bu sınıf bir TearDown metodu ve abstract VerifyMocks metodu içermez.
Moq.MockException : ISecurityService.CreateHash("foo") invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
namespace Foo.Framework.UnitTests
{
/// <summary>
/// Fixture sınıfları için temel sınıf. Strict Mock kullanımını zorunlu kılar. Teardown metodunda VerifyMocks işlemi içerir.
/// </summary>
public abstract class BaseTestFixture : TestFixture
{
/// <summary>
/// VerifyMocks metodunu tetikleyen işlemi. TestFixture'da tanımlanan tüm mock nesnelerin Verify edilmesi hedeflenir.
/// </summary>
[TearDown]
public void TearDown()
{
VerifyMocks();
}
/// <summary>
/// TestFixture'da tanımlanan tüm mock nesnelerin Verify edilmesini hedefleyen soyut metot.
/// Mock nesnelerin VerifyAll() metodunun çağrılması beklenir.
/// </summary>
protected abstract void VerifyMocks();
}
}
[TestFixture]
[Parallelizable(ParallelScope.Fixtures)]
public class CreditCardMailServiceTests : BaseTestFixture
{
//TODO:
}
#region members & setup
StrictMock<IMailServiceRepository> mailServiceRepository;
StrictMock<IMailInfoFactory> mailInfoFactory;
StrictMock<IFooRepository> fooRepository;
StrictMock<ISettings> settings;
CreditCardService service;
[SetUp]
public void Initialize()
{
mailInfoFactory = new StrictMock<IMailInfoFactory>();
mailServiceRepository = new StrictMock<IMailServiceRepository>();
fooRepository = new StrictMock<IFooRepository>();
settings = new StrictMock<ISettings>();
service = new CreditCardService(
mailInfoFactory.Object,
mailServiceRepository.Object,
fooRepository.Object,
settings.Object);
}
#endregion
[Test]
public void SendCreditApplicationMailToMember_SendMailFails_ReturnServerError()
{
// Arrange
var creditApplication = new CreditApplication();
var mailInfo = new MailInfo();
const bool sendMailSuccess = false;
mailInfoFactory.Setup(x => x.CreateCreditApplicationMailInfoForMember(creditApplication)).Returns(mailInfo);
mailServiceRepository.Setup(x => x.SendMail(mailInfo)).Returns(sendMailSuccess);
// Act
var result = service.SendCreditApplicationMailToMember(creditApplication);
// Assert
result.IsSuccess.Should().BeFalse();
result.ResultCode.Should().Be(ResultCode.ServerError);
}
namespace Foo.Framework.UnitTests
{
/// <summary>
/// Strict Mock kullamının gerekmediği TestFixture sınıfları için temel sınıf.
/// </summary>
public class LooseBaseTestFixture : TestFixture
{
}
}
using Moq;
namespace Foo.Framework.UnitTests
{
/// <summary>
/// Birim testleri Fixture sınıfları için temel sınıf.
/// </summary>
public abstract class TestFixture
{
#region verify any
/// <summary>
/// Moq.Mock sınıfları için Verify işlemi uygulandığında geçirilen It.IsAny tipinde parametreler için kullanılır.
/// It.IsAny metodunun hangi bağlamda kullanıldığını tespit etmek amacıyla oluşturulmuştur.
/// </summary>
/// <typeparam name="T">Any olarak kullanılacak tip bilgisi</typeparam>
/// <returns></returns>
public static T VerifyAny<T>()
{
return Any<T>();
}
#endregion
#region setup any
/// <summary>
/// Moq.Mock sınıfları için Setup işlemi uygulandığında geçirilen It.IsAny tipinde parametreler için kullanılır.
/// It.IsAny metodunun hangi bağlamda kullanıldığını tespit etmek amacıyla oluşturulmuştur.
/// </summary>
/// <typeparam name="T">Any olarak kullanılacak tip bilgisi</typeparam>
/// <returns></returns>
public static T SetupAny<T>()
{
return Any<T>();
}
static T Any<T>()
{
return It.IsAny<T>();
}
#endregion
}
}
[TestFixture]
[Parallelizable(ParallelScope.Fixtures)]
public class CreditCardFactoryTests : LooseBaseTestFixture
{
//TODO:
}