My first foray into mocking in a WPF application. The code I'm testing is part of an MVVM ViewModel Method and looks like this:
try
{
var airframesForRegistration = this.UnitOfWork.Airframes.GetAirframesForRegistration(this.SearchRegistration);
this.currentAirframes = new ObservableCollection<Airframe>(airframesForRegistration);
}
catch (Exception ex)
{
this.logger.Error($"Could not access the database", ex);
throw;
}
I want to test that
the error is written to the logger service and
that the exception is thrown.
To do this I'm using XUnit and Moq thus:
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail()
{
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
DataException exception = new DataException();
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
Assert.Throws<DataException>(() => afrvm.GetAirframesForSearchRegistration());
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
The test fails thus:
Message: Moq.MockException :
Expected invocation on the mock exactly 1 times, but was 0 times: x => x.Error("Could not access the database'", System.Data.DataException: Data Exception.)
Configured setups:
ILog x => x.Error("Could not access the database", System.Data.DataException: Data Exception.)
Performed invocations:
ILog.Warn("No Operators found in the database")
ILog.Warn("No airframe statuses found in the database")
ILog.Error("Could not access the database", System.Data.DataException: Data Exception.
at Moq.MethodCall.Execute(Invocation invocation) in C:\projects\moq4\src\Moq\MethodCall.cs:line 120
(NB The extra ILog warnings occur elsewhere in the ViewModel and I was expecting those).
Question
This implies that the error logging was invoked and yet the test fails because it was invoked zero times! How can Moq and XUnit be set up to correctly test for this scenario?
The setup of the logger arguments is the problem.
Differing instances between what is thrown and what is expected mean they wont match when the mock is invoked.
The mock unit of work is throwing a new exception. Not the one you are expecting.
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
DataException exception = new DataException();
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(exception);
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, exception));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, exception), Times.Exactly(1));
}
}
For a looser expectation you could have use It.IsAny<> argument matchers
[Fact]
public void GetAirframesForSearchRegistration_DBAccessFail() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
mock.Mock<IUnitOfWork>()
.Setup(x => x.Airframes.GetAirframesForRegistration("AAAA"))
.Throws(new DataException());
string message = "Could not access the database";
mock.Mock<ILog>()
.Setup(x => x.Error(message, It.IsAny<DataException>()));
var afrvm = mock.Create<AirframesForRegistrationViewModel>();
afrvm.SearchRegistration = "AAAA";
//Act
Action act = () => afrvm.GetAirframesForSearchRegistration();
//Assert
Assert.Throws<DataException>(act);
mock.Mock<ILog>()
.Verify(x => x.Error(message, It.IsAny<DataException>()), Times.Exactly(1));
}
}
Related
I'm using Microsoft.EntityFrameworkCore.InMemory/3.1.16.
my tests pass when I run them individually but when I run all tests some of them fail.
Sdk :.net core 3.1
here is my optionbuilder configuration:
public static DbContextOptionsBuilder<T> GetDbContextOptionsBuilder<T>( string? connectionString =null , Guid? databaseId = null)
where T : DbContext
{
var optionBuilder = new DbContextOptionsBuilder<T>();
if (databaseId is null)
databaseId = Guid.NewGuid();
optionBuilder.UseInMemoryDatabase( $"dbApi-{databaseId:N}",
x => {
x.UseHierarchyId();
} )
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
return optionBuilder;
}
public static DbContextOptionsBuilder<T> GetInDbContextOptionsBuilder<T>( IConfiguration configuration )
where T : DbContext
{
configuration ??= ConfigHelper.GetConfig();
return GetDbContextOptionsBuilder<T>( configuration.GetConnectionString( "DefaultConnection" ) );
}
and how my unit tests call dbContext like this:
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options);
in tests I seed some data like this:
db.SeedTemplate(); //test 1
db.SeedCategory(); //test 2
when I run the tests it throws following error:
The instance of entity type 'entity' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Edit 1:
I add two sample tests:
//This test will pass
[Fact]
public async Task Can_Add_Template_With_Attribute()
{
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options);
//priceAttribute and displayAttribute removed for brevity
template.LinkAttributes( new List<Attribute>{ priceAttribute,displayAttribute} );
await db.Templates.AddAsync( template);
await db.SaveChangesAsync();
var actualTemplate = await db.Templates.FirstAsync();
actualTemplate.ShouldNotBeNull();
actualTemplate.Attributes.First().ShouldEqual( priceAttribute ););
}
//This test will throw error
[Fact]
public async Task Can_Delete_Template()
{
await using var db = new WriteContext(DbContextInitialization.GetDbContextOptionsBuilder<WriteContext>().Options, MediatorHelper.Initialize());
//priceAttribute and displayAttribute removed for brevity
template.LinkAttributes( new List<Attribute>{ priceAttribute,displayAttribute} );
await db.Templates.AddAsync( template);
await db.SaveChangesAsync();
template.Delete();
var command = new DeleteTemplateCommand( template.Id );
var handler = new DeleteTemplateCommand.DeleteTemplateCommandHandler( db );
var result=await handler.Handle( command,CancellationToken.None );
result.Should().BeSuccess();
var actualTemplate = await db.Templates.FirstAsync();
actualTemplate.IsDeleted.ShouldBeTrue();
}
I have one AsyncPeriodicBackgroundWorkerBase base class(DataValidateWorker) which runs 1 minute interval.
I need to send the data I get from the DB to a third party web service and update the results in the db. A Web service response arrives in about 30-40 seconds. For this reason, I need to send Web service queries simultaneously, not sequentially.
For this reason, I wrote code in accordance with parallel programming as seen below. I cannot pull the database connection for the Task I wrote. DB connection closed, I got many errors like Executing.
How can I create the db connection for my Task?
Would it be better to write this job in an external application (exe or service) instead of ABP?
public class DataValidateWorker : AsyncPeriodicBackgroundWorkerBase
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IDataFilter _dataFilter;
public DataValidateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory, IDataFilter dataFilter, IUnitOfWorkManager unitOfWorkManager) : base(timer, serviceScopeFactory)
{
_dataFilter = dataFilter;
_unitOfWorkManager = unitOfWorkManager;
Timer.Period = 60 * 1000; // 60 seconds
}
[UnitOfWork]
protected async override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
try
{
var notificationValidationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<NotificationValidation, int>>();
var notificationValidationItems = await notificationValidationRepository.GetQueryableAsync();
List<NotificationValidation> list = new List<NotificationValidation>();
using (var uow = _unitOfWorkManager.Begin())
{
using (_dataFilter.Disable<IMultiTenant>())
{
list = notificationValidationItems.Where(x => x.RecordDateTime <= DateTime.Now && x.ValidationResult == (int)ValidationResult.NotStarted).ToList();
}
}
NotificationValidationArgs jobArgs = new NotificationValidationArgs();
foreach (var item in list)
{
jobArgs.notificationValidationId = item.Id;
Task taskA = Task.Factory.StartNew(async (Object obj) =>
{
// doing some third party web service operations and db operations
}, jobArgs);
}
}
catch (Exception ex)
{
Logger.LogCritical(2001, ex, DateTime.Now.ToString() + " -> DataValidateWorker -> try 1 -> RDMS uow");
}
}
}
You don't await any of tasks, so lifetime of object ends while your task is still running.
Try to store all of the tasks in a collection and await them before method execution finishes.
Something like below:
public class DataValidateWorker : AsyncPeriodicBackgroundWorkerBase
{
public DataValidateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory)
{
}
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
var tasks = new List<Task>();
foreach (var item in list)
{
tasks.Add(YourLongJob(arg)); // don't await here. collect in a collection
}
await Task.WhenAll(tasks); // wait until all of them is completed.
}
private async Task YourLongJob(object arg)
{
await Task.Delay(30 * 1000); // a long job
}
}
Pls How can I access sqlite database on the webserver in codename one? I can only use database API to access database on the device. In order to access this on the webserver I think is quite different thing. Pls I need a snippet code on this. Thanks
Use the code below, not tested and you may have to adjust it to suite your need. Leave a comment if there's an issue:
ConnectionRequest req = new ConnectionRequest() {
#Override
protected void handleException(Exception ex) {
//handle error
}
};
req.setUrl(YourURL);
req.setPost(true);
req.setHttpMethod("POST"); //Change to GET if necessary
req.setDuplicateSupported(true);
req.addArgument("argumentToSendThroughPostOrGet1", "value1");
req.addArgument("argumentToSendThroughPostOrGet2", "value2");
NetworkManager.getInstance().addToQueueAndWait(req);
if (req.getResponseCode() == 200) {
Map<String, Object> out = new HashMap<>();
Display.getInstance().invokeAndBlock(() -> {
JSONParser p = new JSONParser();
try (InputStreamReader r = new InputStreamReader(new ByteArrayInputStream(req.getResponseData()))) {
out.putAll(p.parseJSON(r));
} catch (IOException ex) {
//handle error
}
});
if (!out.isEmpty()) {
List<Map<String, Object>> responses = (List<Map<String, Object>>) out.get("response");
for (Object response : responses) {
Map res = (Map) response;
System.out.println(res.get("key"));
}
} else {
//handle error
}
} else {
//handle error
}
TEST JSON RESPONSE:
{
"response": [
{
"key": "I was returned",
}
]
}
EDIT:
To pass data from TextField:
req.addArgument("argumentToSendThroughPostOrGet1", myTextField.getText());
Based on your comment, you can read those arguments in PHP as simple as below:
$var1 = $_POST["argumentToSendThroughPostOrGet1"];
$var1 = $_GET["argumentToSendThroughPostOrGet1"]; // if GET method is used in Codename One
//Or use $_REQUEST which supports both methods but not advisable to be used for production
...
And you can use those variables in your php code normally.
Example of Usage with MySql Query:
class Connection {
function connect() {
$mysqli = mysqli_init();
$mysqli->real_connect("localhost", "username", "password", "databaseName") or die('Could not connect to database!');
$mysqli->query("SET NAMES 'UTF8'");
return $mysqli;
}
function close() {
mysqli_close($this->connect);
}
}
$connection = new Connection();
$mysqli = $connection->connect();
$mysqli->query("SELECT * FROM MyTable WHERE ColumnName LIKE '%$var1%' ORDER BY PrimaryKeyId ASC LIMIT 100");
I am creating a silverlight application as a web resource for CRM 2011. Now i am creating a ServiceAppointment record in DB and after creating it i want to change its Status to "reserved" instead of requested.
I googled about this and come across the examples like Close a Service Activity Through Code and Microsoft.Crm.Sdk.Messages.SetStateRequest
They all suggesting to use "SetStateRequest" and for using this i have to set the OptionSetValue like
request["State"] = new OptionSetValue(4);
But above line gives me error saying "OptionSetValue does not contain constructor which takes one argument"
BTW i am using SOAP end point of CRM 2011 service in silverlight application
Any ideas friends?
EDIT
Following is my code
var request = new OrganizationRequest { RequestName = "SetStateRequest" };
request["State"] = 3;
request["Status"] = 4;
request["EntityMoniker"] = new EntityReference() { Id = createdActivityId, LogicalName = "serviceappointment" };
crmService.BeginExecute(request,ChangeActivityStatusCallback,crmService);
And my callback function is
private void ChangeActivityStatusCallback(IAsyncResult result) {
OrganizationResponse response;
try
{
response = ((IOrganizationService)result.AsyncState).EndExecute(result);
}
catch (Exception ex)
{
_syncContext.Send(ShowError, ex);
return;
}
}
You must some how be referencing some other OptionSetValue class that is not the Microsoft.Xrm.Sdk one. Try appending the namespace to see if that resolves your issue:
request["State"] = new Microsoft.Xrm.Sdk.OptionSetValue(4);
Also, why are you using late bound on the SetStateRequest? Just use the SetStateRequest class:
public static Microsoft.Crm.Sdk.Messages.SetStateResponse SetState(this IOrganizationService service,
Entity entity, int state, int? status)
{
var setStateReq = new Microsoft.Crm.Sdk.Messages.SetStateRequest();
setStateReq.EntityMoniker = entity.ToEntityReference();
setStateReq.State = new OptionSetValue(state);
setStateReq.Status = new OptionSetValue(status ?? -1);
return (Microsoft.Crm.Sdk.Messages.SetStateResponse)service.Execute(setStateReq);
}
Thanks Daryl for you time and effort. I have solved my problem with the way u have suggested.
I am posting my code that worked for me.
var request = new OrganizationRequest { RequestName = "SetState" };
request["State"] = new OptionSetValue { Value = 3 };
request["Status"] = new OptionSetValue { Value = 4 };
request["EntityMoniker"] = new EntityReference() { Id = createdActivityId, LogicalName = "serviceappointment" };
crmService.BeginExecute(request,ChangeActivityStatusCallback,crmService);
private void ChangeActivityStatusCallback(IAsyncResult result) {
OrganizationResponse response;
try
{
response = ((IOrganizationService)result.AsyncState).EndExecute(result);
}
catch (Exception ex)
{
_syncContext.Send(ShowError, ex);
return;
}
}
I have written a working class in Apex. It is an Email service extender, that processes incoming emails.
It is working perfect on my sandbox enviroment.
I have created a test class, so I can also deploy it to my production, but when validating the code, I get the only 72% of my code is tested.
This is my main class
global class inboundEmail implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
Lead lead;
String [] mFromUserParams;
String [] sourceText;
String mCaseObject;
try{
sourceText = email.toAddresses[0].split('#');
String [] mParams = sourceText[0].split('\\.');
**// FROM THIS LINE TO THE END - NOT COVERED**
mFromUserParams = email.fromAddress.split('#');
mCaseObject = mParams[0];
if (mCaseObject == 'lead'){
lead = new Lead();
lead.LastName = mFromUserParams[0];
lead.Company = email.fromAddress;
lead.OwnerId = mParams[1];
lead.LeadSource = mParams[2];
lead.Email = email.fromAddress;
lead.RequirementsDescription__c = email.subject + email.plainTextBody;
insert lead;
result.success = true;
} else if (mCaseObject == 'case'){
result.success = true;
} else {
result.success = false;
}
}catch(Exception e){
result.success = false;
result.message = 'Oops, I failed.';
}
return result;
}
}
This is my test class
#isTest
private class inboundEmailTest {
public static testMethod void inboundEmail(){
// Create a new email, envelope object and Header
Messaging.InboundEmail email = new Messaging.InboundEmail();
Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope();
envelope.toAddress = 'lead.owner.new#cpeneac.cl.apex.sandbox.salesforce.com';
envelope.fromAddress = 'user#acme.com';
email.subject = 'Please contact me';
email.fromName = 'Test From Name';
email.plainTextBody = 'Hello, this a test email body. for testing Bye';
// setup controller object
inboundEmail catcher = new inboundEmail();
Messaging.InboundEmailResult result = catcher.handleInboundEmail(email, envelope);
}
}
According to the error message, ALL lines in the Try/Catch block from the 3rd line are not covered. (marked in the code).
in your test method you're setting envelope.toAddress but in your email service you're splitting the first element of the actual InboundEmail objects toAddresses. this probably causes either an ArrayIndexOutOfBoundsException or a NPE because the element 0 does not exist. so the code coverage will be poor because your test always jumps into the exception handling and leaves the rest of you code uncovered. just set the emails toAddresses and you should have a better coverage.
h9nry
In your test code, can you add a scenario that causes the lead insert to fail? This will cause the code in your catch block to execute and provide you the needed code test coverage.
The email.fromAddress is not a list by default, so just setting that to a string and not a list solved this.