How to mock SendGrid - c#

I am trying to write a unit test for a method I wrote which sends out an email with SendGrid.
My method is something like this:
public async Task SendEmail(TemplatedMailMessage emailMessage)
{
if (String.IsNullOrWhiteSpace(emailMessage.Html) || String.IsNullOrWhiteSpace(emailMessage.From.ToString()) || !emailMessage.To.Any())
{
throw new Exception("Html, From or To is empty");
}
try
{
// Send the email
await this.TransportWeb.DeliverAsync(emailMessage.GetSendGridMessage());
}
catch (Exception ex)
{
//do stuff
}
//log success
}
the TransportWeb is a property which is set in my constructor through a parameter so I can create a mock object.
public EmailManager(Web transportWeb = null)
{
this.TransportWeb = transportWeb ?? SetupSendGrid();
}
In my test method I am trying to mock the TransportWeb (of type SendGrid.Web) property:
[TestMethod]
public async Task SendEmail_ValidEmailTemplateAndNoParameters_EmailIsSent()
{
//ARRANGE
var templatedMailmessage = new Mock<TemplatedMailMessage>();
var transportWeb = new Mock<Web>();
transportWeb.SetupAllProperties();
transportWeb.Setup(x => x.DeliverAsync(It.IsAny<ISendGrid>()));
var emailManager = new EmailManager(transportWeb.Object);
//ACT
await emailManager.Send(templatedMailmessage.Object);
//ASSERT
transportWeb.Verify(x => x.DeliverAsync(It.IsAny<ISendGrid>()), Times.Once());
}
However, I get the following error:
Invalid setup on a non-virtual (overridable in VB) member: x =>
x.DeliverAsync
Does anybody have an idea how I can fix this?

Okay I fixed it :)
You should not use the Web class but ITransport interface:
var transport = new Mock<ITransport>();
transport.SetupAllProperties();
transport.Setup(x => x.DeliverAsync(It.IsAny<SendGridMessage>())).ReturnsTask();
var em = new EmailManager(transport.Object);
I also used the extensions methods created by Simon V.

Related

Moq.MockException: Invocation failed with mock behavior Strict

In the below code, I'm using Moq to test if my Post method is returning CreatedAtRoute StatusCode on successfully addind a book but the test case is failing in Act part when it calls the service and shows the following error:
Moq.MockException: 'IEventRepository.AddEvent(TEMS.Business.Entities.Event) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.'
Test Setup and Tear Down:
private Mock<IBookRepository> mockBookRepository;
public override void TestSetup()
{
mockBookRepository = this.CreateAndInjectMock<IBookRepository>();
Target = new BooksController(mockBookRepository.Object);
}
public override void TestTearDown()
{
mockBookRepository.VerifyAll();
}
TestCase:
[Fact]
public void AddBook_ReturnCreatedAtRoute()
{
// to prevent autofixture throwing ThrowingRecursionBehavior
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
var books = fixture.Create<Book>();
this.mockBookRepository.Setup(s => s.AddBook(books)).Returns(1);
// Act
BookInputModel booksViewModel = convertDbModelToInputModel(books);
var actual = Target.Post(booksViewModel);
// Assert
var actionResult = Assert.IsType<ObjectResult>(actual);
Assert.Equal((int)HttpStatusCode.Created, actionResult.StatusCode);
this.mockBookRepository.Verify(v => v.AddBook(books), Times.Once);
}
Post Method:
public ActionResult Post(BookInputModel request)
{
try
{
Book addBooks = convertDbModelToInputViewModel(request);
var result = _service.AddBook(addBooks);
if (result == 0) return BadRequest();
return CreatedAtRoute("GetBook", new { id = addBooks.Id }, request);
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
Repository:
public int AddBook(Book request)
{
Book newBook = new Book()
{
Name = request.Name,
Author = request.Author,
Publication = request.Publication,
TotalPages = request.TotalPages,
};
if (newBook == null) return 0;
else
{
_context.Book.Add(newBook);
_context.SaveChanges();
return 1;
}
}
Took me a while to realise this little error that was causing the problem - the problem was happening in Act part for the very valid reason as I'm doing setup using DbModel and in Act I'm passing ViewModel(as the Post method in controller requires it) which raises the conflict. Now as far as I'm aware of, the only way to resolve it by using the same model in Post method as of Repository.

How do I unit test Service Bus SendAsync method properly?

When running the unit test for Azure Service Bus method SendAsync I get the following exception on the line _queueClient.Verify():
Expected invocation on the mock at least once, but was never performed: x => x.SendAsync({MessageId:})
This is my unit test:
public CustomerSendMsg(){ } //ctor
public async Task ShouldSendMessage()
{
var _queueClient = new Mock<IQueueClient>();
var _sut = new Publisher(_queueClient.Object);
var customer = new Customer()
{
FirstName = "John",
LastName = "Doe"
};
_queueClient.Setup(t => t.SendAsync(It.IsAny<Message>())).Returns(Task.CompletedTask).Verifiable();
await _sut.SendMessageAsync(customer);
var messageBody = JsonSerializer.Serialize(customer);
var msg = new Message(Encoding.UTF8.GetBytes(messageBody));
_queueClient.Verify(t = > t.SendAsync(msg));
}
This is SendMessageAsync method from the Publisher class:
public async Task SendMessageAsync<T>(T obj)
{
var messageBody = JsonSerializer.Serialize(obj);
var msg = new Message(Encoding.UTF8.GetBytes(messageBody));
await _queueClient.SendAsync(msg);
}
Do you have any idea how can I make this unit test work?
Instead of creating a new message object which wouldn't work because the references are different - you could decode the body property from the Message class and verify the customer object's properties.
Instead of doing an object comparison (which is what Moq is doing), you can do the verification that the object being passed to the method has specific properties of the object using It.Is<T>.
_queueClient.Verify(t = > t.SendAsync(It.Is<byte[]>(x => x.Body == Encoding.UTF8.GetBytes(messageBody)));

How to verify that the mock doesn't return a specific return type

I'm trying to call an Api. If the call fails, I'm retrying with in the configured time out. When I'm writing the unit test for the scenario where the api repeated calls fails and the time out happens. I'm unable to determine what to Assert here. I think it's obvious to verify that the mockedBaseApiClient has never returned the response success for any of it's call in the execution. But I can't figure out how to verify the return type on the mocked object.
public async Task UpdateDocumentPath(UpdateDocument doc)
{
var path = $"v1/documents/{doc.accountId}/{doc.documentId}/paths";
try
{
var response = await _baseApiClient.PutAsync(DocumentsApiKey, path, doc);
if (response.IsSuccessStatusCode)
{
var updatedDocument = await response.Deserialize<UpdateDocument>();
var updatedPath = updatedDocument.documentPath;
}
else
{
await TryToReConnect(doc);
}
}
catch (Exception ex)
{
_exceptionPublisher.PublishException(ex);
await TryToReConnect(doc);
}
}
My Unit Test:
[Fact]
public async Task DocumentPatchAdapter_PatchDocument_RetriesWhenUnSuccessfullStatusCode_ButTimeoutExhausts_Tests()
{
MockConfiguration();
_mockedConfigurationSection.SetupGet(c => c.Value).Returns("20000");
HttpResponseMessage notFoundResponse = new HttpResponseMessage
{
StatusCode = HttpStatusCode.NotFound
};
_mockedBaseApiClient
.SetupSequence(c => c.PutAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Object>()))
.ReturnsAsync(notFoundResponse)
.ReturnsAsync(notFoundResponse)
.ReturnsAsync(notFoundResponse);
UpdateDocument documentPathToBeUpdated = new UpdateDocument
{
accountId = 34512274,
documentPath = "\\\\sharoi.vliproj.com\\DevL\\DocumentMaker\\Print\\InsuranceScoreRenew_19196100600.pdf",
documentId = 3656261
};
await _documentPatchAdapter.UpdateDocumentPath(documentPathToBeUpdated);
//_mockedBaseApiClient.Verify(c=>c.PutAsync(.....)).NeverReturns(SuccessCode); I want something like this here
}

How moq File.Delete (IFileSystem) in app MVC Moq

My method which work
[HttpPost]
public async Task<ActionResult> DeleteTeam(int id)
{
Team team = await teamRepository.DeleteTeamAsync(id);
var fileToDeletePath = Path.Combine(Server.MapPath("~/Images/NBAlogoImg/"), team.Path);
if (System.IO.File.Exists(fileToDeletePath))
{
System.IO.File.Delete(fileToDeletePath);
}
if (team != null)
{
TempData["message"] = string.Format("{0} был удален", team.Name);
}
return RedirectToAction("Index", "Player");
}
It's my attempt to make a test, but unsuccessful
[TestMethod]
public async Task CanDeletePlayerAsync()
{
//Arrange
Mock<ITeamRepository> teamsMock = new Mock<ITeamRepository>();
Team team2 = new Team { Id = 2, Name = "Boston" , Path = "CHi.png" };
Team team3 = new Team { Id = 3, Name = "Lakers" };
string fullPath = ("~/Images/NBAlogoImg/");
var serverMock = new Mock<HttpServerUtilityBase>();
serverMock.Setup(x => x.MapPath(fullPath)).Returns(#"s:\work");
var httpContextMock = new Mock<HttpContextBase>();
httpContextMock.Setup(x => x.Server).Returns(serverMock.Object);
var mockFile = new Mock<IFileSystem>();
TeamController controller = new TeamController(teamsMock.Object);
controller.ControllerContext = new ControllerContext(httpContextMock.Object, new RouteData(), controller);
teamsMock.Setup(m => m.DeleteTeamAsync(team2.Id)).Returns(Task.FromResult(team2));
// Act
ActionResult result = await controller.DeleteTeam(team2.Id);
mockFile.Verify(x => x.File.Delete(#"s:\work\file.png"));
//Assert
Assert.IsInstanceOfType(result, typeof(RedirectToRouteResult));
}
I add the funcional to delete image from app if I delete the team. It works perfect, but how make a test by Moq I try some attempts by unsuccessfully.
I have the error message
Expected invocation on the mock at least once, but was never performed: x => x.File.Delete("s:\work\file.png")
No setups configured.
No invocations performed.
how it fix? I have downloaded IFileSystem and made a moq but verify have been not work.
One obvious solution would be to wrap your File.Delete Call in Custom Class, which implements a Custom interface, For example,
public interface IFileOperations
{
void Delete(string path);
}
For your System Operations, you can create a wrapper class.
public class SystemFileOperations:IFileOperations
{
public void Delete(string path)
{
File.Delete(path);
}
}
Now you can alter your original code to ensure SystemFileOperations is injected at all places where you would require IFileOperations.Delete.
private IFileOperations _fileOperations;
public ControllerName(IFileOperations operations)
{
_fileOperations = operations;
}
Following line would be then replaced
System.IO.File.Delete(fileToDeletePath);
with
_fileOperations.Delete(fileToDeletePath);
And for mocking , you could
var mock = new Mock<IFileOperations>();
mock.Verify(x=>x.Delete(path),Times.AtLeastOnce());
Please note that in your case, due to usage of File.Exists, you might have to mock that as well following the same pattern if you desire so

Moq WebApi async method

I'm new to testing with Moq, so I'm confused a bit regarding ReturnsAsync.
Here is my test method, where I'm expecting ReturnsAsync should receive type that will be returned by method defined in Setup. Am I right?
But seemed ReturnsAsync should have the other signature - it's looking for Func delegate.
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(new Task<IEnumerable<TourObjectDTO>>);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.AreSame(typeof(TourObjectViewModel), result);
}
}
Method under test:
public async Task<IEnumerable<TourObjectViewModel>> Get()
{
IEnumerable<TourObjectViewModel> viewmodel = null;
try
{
var tourobjects = await _tos.GetAll();
viewmodel = Mapper.Map<IEnumerable<TourObjectViewModel>>(tourobjects);
}
catch (Exception ex)
{
Log.ErrorFormat("Method:{0} <br/> Error: {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, ex);
}
return viewmodel;
}
Pass an actual result.
Assuming ITourObjectService.GetAll() returns Task<IEnumerable<TourObjectDTO>>
eg
[TestClass]
public class TourObjectControllerTest
{
[TestMethod]
public async Task GetTourObjects()
{
var fakeData = new List<TourObjectDTO>() {
new TourObjectDTO { ... }
};
var mockService = new Mock<ITourObjectService>(MockBehavior.Default);
mockService.Setup(x => x.GetAll()).ReturnsAsync(fakeData);
var controller = new TourObjectController(mockService.Object);
var result = await controller.Get();
Assert.IsNotNull(result);
Assert.IsTry(typeof(IEnumerable<TourObjectViewModel>).IsAssignableFrom(result.GetType());
}
}
ReturnsAsync() should take a return value as the paramter instead of Task<TReurnValue>. For example, next 2 lines of code are equal (by behavior):
mockService.Setup(x => x.GetAll()).ReturnsAsync(new List<TourObjectDTO>());
mockService.Setup(x => x.GetAll()).Returns(Task.FromResult(new List<TourObjectDTO>()));
You need to replace new List<TourObjectDTO>() with your test data which you want to retrieve in the test from the mock. For example, you can create just few test values:
var testData = new [] { new TourObjectDTO(1, "test1"), new TourObjectDTO(2, "test2") };
mockService.Setup(x => x.GetAll()).ReturnsAsync(testData);
or create fake data generator if needed.
Note that ReturnsAsync() is only available for methods that return a Task<T>. For methods that return only a Task, .Returns(Task.FromResult(default(object))) can be used.

Categories