I'm new to programming and c#, and have been assigned the task of writing unit tests for legacy code in preparation for big DB change.
The more I read on unit testing, the more skeptical I am on my approach.
How would you approach writting a unit test for the following?
Currently I've just been unit testing my Data Access Layer Methods, ensuring they return back a result? But apparently unit test are supposed to be independent of any outside environments?
How can I test my application, when almost everything is making a call to the database or stored procedure?
My Form Code:
public void button1_Click(object sender, EventArgs e)
{
LoadAllCountries()
}
private static void LoadAllCountries()
{
List<nsHacAppMod.ViewCountryInfo> oReturn = moBusinessServices.GetAllCountries();
}
My Data Access Layer
public List<nsAppMod.ViewCountryInfo> GetAllCountries()
{
List<nsAppMod.ViewCountryInfo> oReturn;
var oResult = from c in moDataContext.ViewCountryInfos
select c;
oReturn = oResult.ToList();
return oReturn;
}
My current unit test for this code, is this acceptable? If not what would you test?
[Test]
public void LoadAllCountries()
{
hac.CatalogSamples cs = new hac.CatalogSamples();
var countries = cs.GetAllCountries().count();
Assert.GreaterOrEqual(countries 0);
}
You can unit test data access things if your context is based off of an interface like IContext it can be faked, and you can then test just the code you are trying to test like the GetAllCountries() method.
However, other than the try catch, there is no actual logic to test. I look at it like this: If it is a property that has no logic in it, or methods too, it's not worth testing.
Using a mocking framework like FakeItEasy I would test this method just like this:
public class ClassToTest
{
internal virtual MoDataContext CreateContext()
{
return new MoDataContext();
}
public List<ViewCountryInfo> GetAllCountries()
{
List<ViewCountryInfo> oReturn = null;
try
{
var oResult = from c in this.CreateContext().ViewCountryInfos
select c;
oReturn = oResult.ToList();
}
catch (Exception)
{
}
return oReturn;
}
[Fact]
public void Test_GetAllCountries_ResultCountShouldBeGreaterThan0()
{
var fakeData = new List<ViewCountryInfo>();
fakeData.Add(new ViewCountryInfo());
var sut = A.Fake<ClassToTest>();
A.CallTo(sut).CallsBaseMethod();
A.CallTo(() => sut.CreateContext()).Returns(fakeData);
var result = sut.GetAllCountries();
Assert.IsTrue(result.Count() > 0);
}
Related
Moq doesn't match the mocked method.
Exception:
Exception thrown: 'Moq.MockException' in Moq.dll:
'IMongoRepository.FindByVrcId("b4cb3139-90aa-4477-979b-d893e3317386")
invocation failed with mock behavior Strict. All invocations on the
mock must have a corresponding setup.'
This is my unit test:
public class OfferHandlerTest : TestBase
{
Mock<IMongoRepository> repositoryMock = new Mock<IMongoRepository>(MockBehavior.Strict);
OfferHandler? offerHandler;
[Fact]
public void HandleTest()
{
JObject? offerFullDocument = null;
using (var sr = new StreamReader("Data/offer_full_document.json"))
{
var reader = new JsonTextReader(sr);
offerFullDocument = JObject.Load(reader);
}
var kafkaPayload = new KafkaMessagePayloadSourceConnector();
kafkaPayload.OperationType = Constants.Mongo_OperationType_Update;
kafkaPayload.FullDocument = offerFullDocument;
OfferService service = new OfferService(repositoryMock.Object);
offerHandler = new OfferHandler(service, this.ServiceConfiguration);
offerHandler.Handle(kafkaPayload);
DHOffer offer = new DHOffer();
offer.Version = 1;
// THIS SETUP FAILS
repositoryMock.Setup(s => s.FindByVrcId<DHOffer>(It.IsAny<string>())).Returns(It.IsAny<DHOffer>());
repositoryMock.Verify(s => s.FindAndUpdate<DHOffer>(It.IsAny<DHOffer>()), Times.Once);
}
}
This is the handle method:
public void Handle(KafkaMessagePayloadSourceConnector kafkaPayload)
{
VRCOffer offer = kafkaPayload!.FullDocument!.ToObject<VRCOffer>()!;
if (kafkaPayload!.OperationType!.Equals(Constants.Mongo_OperationType_Update))
{
offerService.updateOfferStatus(OfferMapper.MapToDataHubModel(offer), offer.MasterId);
}
}
And finally the service method:
public class OfferService
{
private readonly IMongoRepository offerRepository;
public OfferService(IMongoRepository offerRepository)
{
this.offerRepository = offerRepository;
}
internal void updateOfferStatus(DHOffer offer, string vrcMasterId)
{
// THIS SHOULD RETURN MOCKED OBJECT
DHOffer existingOffer = offerRepository.FindByVrcId<DHOffer>(vrcMasterId);
existingOffer.ModifiedDate = DateTime.Now.ToString();
existingOffer.AdditionalInformation.Status = offer?.AdditionalInformation?.Status!;
offerRepository.FindAndUpdate(existingOffer);
}
}
I tried using It.IsAny<DHOffer>() in the Return() method but I get the same exception.
Your issue is that you are running the Moq setup after the mocked object has been used. By then it's already too late. You just need to run the .Setup(...) commands at the start.
It's also worth noting that using a shared mock can be problematic if multiple tests need the setups to be different. Some tools can run tests in parallel which may screw things up, or in a different order which can cause things to be more brittle.
I'm working on a legacy project which is created with .NET Framework 4.8, locator pattern, and business factory to instantiate businesses.
I'm trying to add unit tests using MSTest and MOQ.
The SubmitEntities method in mocked business does not run validations. I mean when I run a test that is expected to throw an exception, it passes successfully. I have no idea why is this happening.
Also, I don't have experience in writing unit tests and this is my first time.
In short, I'm expecting to get exception when the test is run, but it finishes with submittedBalanceMappings with a count of more than zero.
The test class constructor is:
private readonly BalanceMappingBusinessService _balanceMappingBusinessService;
private readonly Mock<IBalanceMappingBusiness> _mockBalanceMappingBusiness = new Mock<IBalanceMappingBusiness>();
public BalanceMapping()
{
_balanceMappingBusinessService = new BalanceMappingBusinessService(_mockBalanceMappingBusiness.Object);
}
The test is:
[TestMethod]
public async Task SubmitEntities_NoDataWillBeSubmitted_IfColumnSettingIdDoesNotExist()
{
// Arrange
await LoginWithSuperAdminAccount();
var submittingBalanceMappings = new List<BalanceMappingEntity>
{
new BalanceMappingEntity
{
AdjustedBalanceDetailId = adjutedBalanceDetailId,
ColumnSettingId = Guid.NewGuid(),
EntityState = EntityState.Added
}
};
var submittedBalanceMappings = new List<BalanceMappingEntity>();
_mockBalanceMappingBusiness.Setup(
bmb => bmb.SubmitEntities(submittingBalanceMappings))
.Callback((List<BalanceMappingEntity> balanceMappings) => submittedBalanceMappings.AddRange(balanceMappings));
var adjutedBalanceDetailId = GetAdjustedBalanceDetail();
//Act
_balanceMappingBusinessService.SubmitEntities(submittingBalanceMappings );
// Assert
Assert.IsTrue(submittedBalanceMappings.Count == 0);
}
The SubmitEntities method is:
public void SubmitEntities(List<BalanceMappingEntity> balanceMappings)
{
try
{
ValidateSubmittingBalanceMappings(balanceMappings);
using var onlineUnitOfWork = new OnlineUnitOfWork();
foreach (var balanceMapping in balanceMappings)
{
balanceMapping.AccountId = ReusableObjects.Current.CurrentAuthenticatedAccountId;
onlineUnitOfWork.Submit(balanceMapping);
}
onlineUnitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
I have a class that I am trying to test out through writing some unit test in C#. I am also using Moq library in my unit test for mocking.
Below are some of the methods in my class which I want to mock so that they can return some dummy data which I want to return always.
public class DataManager
{
public DataManager(ILoggingService loggingService, IConfiguration configuration)
{
// ...
}
// how to mock this to return dummy data?
private DataHolder GetData()
{
//... return DataHolder object
}
// how to mock this to return dummy data?
private IEnumerable<ProcessClient> GetClientValues(string value)
{
//... return IEnumerable<ProcessClient> object
}
}
This is how I am testing it out:
private Mock<ClientUtility> utility;
[SetUp]
public void Setup()
{
utility = new Mock<ClientUtility>();
}
[Test]
public void Test1()
{
// what should I do here so that "GetData" and "GetClientValues" return dummy data when I call below class constructor?
var testName = "config1";
var testContent = "{}";
utility.Setup(x => x.Add(new DataStatus { Pin = "test" }, "data.com", "test")).Returns(Task.FromResult(true));
var dataManager = new DataManager(loggingService.Object, configuration.Object);
Assert.AreEqual(testContent, dataManager.GetStuff(testName)[0]);
}
Question:
How can I mock "GetData" and "GetClientValues" using Moq or any other library to return dummy data from those two methods? Is this possible to do?
I think you can use CallBase = true.
So your mock will behave like true class.
var dataManager = new Mock<DataManager>(loggingService.Object, configuration.Object) { CallBase = true };
dataManager.Object.Method1();
dataManager.Object.Method2();
And you can mock some methods like you do with utility.
Search by "CallBase" on page:
https://github.com/Moq/moq4/wiki/Quickstart
UPDATE
I made methods public to call them.
And there is some issue, that i didn't see last time. You need to make "virtual" methods if you want change their behavior.
There is example of code. As you can see, GetClientValues() returns real data because we call default real implementation with CallBase = true and GetData() returns dummy data because we mocked its behavior.
public class DataManager
{
public DataManager(ILoggingService loggingService, IConfiguration configuration)
{
// ...
}
// how to mock this to return dummy data?
public virtual DataHolder GetData()
{
return new DataHolder
{
Data = "RealData"
};
}
// how to mock this to return dummy data?
public IEnumerable<ProcessClient> GetClientValues(string value)
{
return new List<ProcessClient>
{
new ProcessClient
{
Data = "RealData"
}
};
}
}
And tests.
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
var loggingService = new Mock<ILoggingService>();
var config = new Mock<IConfiguration>();
var dataManager = new Mock<DataManager>(loggingService.Object, config.Object){CallBase = true};
dataManager.Setup(x => x.GetData()).Returns(new DataHolder { Data = "FakeData" });
Assert.AreEqual("RealData", dataManager.Object.GetClientValues("test").First().Data);
Assert.AreEqual("FakeData", dataManager.Object.GetData().Data);
}
}
But anyway i think, that this approach isn't good for testing.
There is a problem with testing private methods. We have some ways (for example Invoke()), but in general, "if you want to unit test a private method, something may be wrong". I would like to recommend you to read something about test design and writing testable code.
About testing private methods How do you unit test private methods?.
I'm moving first steps in TDD (I'm studying "Professional Test-Driven Development with C#" by Bender and McWherter).
I'm trying to write my first application with TDD: I assume to have a DataService class for manage persistence. I wrote two tests that pass, but I think I've not got the point.
This is my first test, I assume that I can instantiate a Transaction, whatever it means in the DataService
public void Begin_a_transaction_should_returns_true_when_is_all_ok()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
DataService sut = new DataService(dataLayer.Object);
bool expected = true;
dataLayer.Setup(dl => dl.BeginTransaction()).Returns(expected);
bool actual = sut.BeginTransaction();
Assert.AreEqual(expected, actual);
}
According to TDD now I wrote the classes, no issues with this
public class DataService
{
private IDataLayer _dataLayer;
public DataService(IDataLayer dataLayer)
{
this._dataLayer = dataLayer;
}
public bool BeginTransaction()
{
return _dataLayer.BeginTransaction();
}
}
Now I want to write second test: BeginTransaction should fail if transaction already exists, and I demand this to IDataLayer
[Test]
public void Begin_a_transaction_should_throws_exception_if_transaction_already_exists()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
DataService sut = new DataService(dataLayer.Object);
dataLayer.Setup(dl => dl.BeginTransaction()).Throws(new Exception("Transaction already exists"));
Assert.Throws<Exception>(() => sut.BeginTransaction());
}
And now the point: tests pass without writing any line of code, because I've mocked the BeginTransaction to throw an Exception.
That can be OK because I'll test it in the implementation of IDataLayer tests, but if I mock all, DataService tests are useful?
I would say this happens because you're testing the behavior of a class that does not have any behavior other than wrapping the IDataLayer - anything that happens in the wrapped class is simply passed on to the outside. The same thing that you are describing happens for the case when the method returns true although it's less obvious.
To make your example more meaningful you could add some behavior that is dependent on the result of IDataLayer's BeginTransaction(), like;
Implementation
public class DataService
{
public DataService(IDataLayer dataLayer, IBookRepository bookRepository)
{
_dataLayer = dataLayer;
_bookRepository = bookRepository;
}
public void StoreBookInfo(string data)
{
if (_dataLayer.BeginTransaction())
_bookRepository.StoreBookInfo(data);
else
throw new SomeException();
}
}
Test
[Test]
public void Should_store_book_info_if_transaction_can_be_started()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
Mock<IBookRepository> bookRepository = new Mock<IBookRepository>();
dataLayer.Setup(dl => dl.BeginTransaction()).Returns(true);
bookRepository.Setup(x => x.StoreBookInfo(It.IsAny<string>()));
DataService sut = new DataService(dataLayer.Object, bookRepository.Object);
sut.StoreBookInfo("SomeValue");
bookRepository.Verify(x => x.StoreBookInfo(It.IsAny<string>()));
}
[Test]
public void Should_throw_exception_if_transaction_cannot_be_started()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
Mock<IBookRepository> bookRepository = new Mock<IBookRepository>();
dataLayer.Setup(dl => dl.BeginTransaction()).Returns(false);
DataService sut = new DataService(dataLayer.Object, bookRepository.Object);
Assert.Throws<SomeException>(() => sut.StoreBookInfo("someValue"));
}
Exposing internals just for the sake of tests generally is not good practice. In your case _transaction is made protected so that decorator could do changes to it's internal state. I'd better suggest to mimic behavior of a client.
[Test]
public void Begin_a_transaction_should_throws_exception_if_transaction_already_exists()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
DataService sut = new DataService(dataLayer.Object);
sut.BeginTransaction();
Assert.Throws<Exception>(() => sut.BeginTransaction());
}
Well, I reviewed my code in this way.
public class DataService
{
private IDataLayer _dataLayer;
protected object _transaction;
public DataService(IDataLayer dataLayer)
{
this._dataLayer = dataLayer;
}
public bool BeginTransaction()
{
if (_transaction != null)
throw new Exception("Transaction already exists");
_transaction = _dataLayer.BeginTransaction();
return true;
}
}
Than I use a decorator pattern in test class
internal class DataServiceDecorator : DataService
{
public DataServiceDecorator(IDataLayer dataLayer) : base(dataLayer)
{ }
public void InjectTransactionObject(object transaction)
{
this._transaction = transaction;
}
}
So I'm able to write this test
[Test]
public void Begin_a_transaction_should_throws_exception_if_transaction_already_exists()
{
Mock<IDataLayer> dataLayer = new Mock<IDataLayer>();
DataServiceDecorator sut = new DataServiceDecorator(dataLayer.Object);
sut.InjectTransactionObject(new object());
Assert.Throws<Exception>(() => sut.BeginTransaction());
}
What do you think about this?
I have a method whose operations depend on its dependents, like below. Is it still value doing unit test? Because the unit test is not testing any business logic, rather by mocks.
Unit test below:
Please note that the method's opetions is determined by expectedCustomerValidality, which is setup by the test. Mostly, the logic is determined by mocks (e.g. Setup(c => c.IsValid()).
[Test]
[TestCase(true)]
[TestCase(false)]
public void AddCustomer(bool expectedCustomerValidality)
{
//using Moq
companyRepositoryMock.Setup(c => c.GetById(It.IsAny<int>())).Returns(new Company());
customerValidatorMock.Setup(c => c.IsValid(It.IsAny<Customer>())).Returns(expectedCustomerValidality);
var customer = new Customer
{
Firstname = "firstName",
Surname = "surname",
Company = new Company { Id = 1 }
};
var addCustomer = customerServiceSut.AddCustomer(customer);
Assert.AreEqual(expectedCustomerValidality,addCustomer);
}
Production code below:
public class CustomerService : ICustomerService
{
private ICompanyRepository companyRepository;
private ICustomerRepository customerRepository;
private ICustomerValidator customerValidator;
public CustomerService(ICompanyRepository companyRepository, ICustomerRepository customerRepository, ICustomerValidator customerValidator)
{
this.companyRepository = companyRepository;
this.customerRepository = customerRepository;
this.customerValidator = customerValidator;
}
public bool AddCustomer(Customer customer)
{
customer.Company = companyRepository.GetById(customer.Company.Id); ;
if (customerValidator.IsValid(customer))
{
customerRepository.AddCustomer(customer);
return true;
}
return false;
}
}
Questions:
Is AddCustomer() required unit-testing?
If so, is the current unit test performing the correct way?
1 If not, what is the proper way to unit test it?
I don't like this. Here's why: your test method names should reflect the method under test, what the preconditions of the test are, and what you expect the result of the test to be:
public bool AddCustomer_CustomerIsValid_ShouldReturnTrue()
public bool AddCustomer_CustomerIsInvalid_ShouldReturnFalse()
Now if you want to, you can refactor the core testing logic into its own method to eliminate the code duplication, and then call that method from the two above methods. But the refactored method is not a test case; it's just a helper method for the actual test cases.
Example:
[Test]
public void AddCustomer_CustomerIsValid_ShouldReturnTrue()
{
var result = AddCustomerTest(true);
Assert.IsTrue(result);
}
[Test]
public void AddCustomer_CustomerIsInvalid_ShouldReturnFalse()
{
var result = AddCustomerTest(false);
Assert.IsFalse(result);
}
public void AddCustomerTest(bool expectedCustomerValidality)
{
//using Moq
companyRepositoryMock.Setup(c => c.GetById(It.IsAny<int>())).Returns(new Company());
customerValidatorMock.Setup(c => c.IsValid(It.IsAny<Customer>())).Returns(expectedCustomerValidality);
var customer = new Customer
{
Firstname = "firstName",
Surname = "surname",
Company = new Company { Id = 1 }
};
var result= customerServiceSut.AddCustomer(customer);
return result;
}
AddCustomer requires testing, as it performs a business logic - it retrieves a company, and sets it, it validates the data, it adds the data to the repository, and it returns a result.
Your test is almost correct. I would make it a little bit more specific, to verify all the business logic:
[Test]
[TestCase(true)]
[TestCase(false)]
public void AddCustomer(bool isCustomerValid)
{
//arrange
//NOTE!!! create the mocks with MockBehavior.Strict
//that way the test will fail if a call is made w/o setup
const long testCompanyId = 100;
var testCompany = new Company{ Id = testCompanyId };
companyRepository.Setup(r => r.GetById(testCompanyId))
.Returns(testCompany);
var testCustomer = new Customer
{
Firstname = "firstName",
Surname = "surname",
Company = new Company { Id = testCompanyId }
};
customerValidatorMock.Setup(c => c.IsValid(It.Is<Customer>(c => c == testCustomer && c.Company == testCompany)).Returns(isCustomerValid);
if (isCustomerValid)
{
customerRepository.Setup( r => r.AddCustomer(testCustomer) ). Verifiable();
}
//act
var addCustomerResult = customerServiceSut.AddCustomer(testCustomer);
//assert
Assert.AreEqual(isCustomerValid, addCustomerResult);
cutomerRepository.VerifyAll();
}
The above test will test all the functionality of the service method, including if the company is fetched by id, and if the fetched one is used. Also, it will verify if the customer is added, if it is valid. The test will fail, if the customer is added if not valid (as the customerRepository is created with MockBehavior.Strict, and a setup will not be performed).