I'm working on a ASP.NET Core 2.1 Api controller that returns a FileStreamResult. My business layer returns a MemoryStream object that has been rewound to the start position. What I am trying to do is write a unit test that checks if the expected MemoryStream is return from the method. However, when I attempt this, the test method just hangs. I'm using Automapper, NSubstitute and Xunit for mapping, mocking, and testing respectively.
//Action Method
[Route("Excel")]
[HttpPost]
[ProducesResponseType(typeof(string), 200)]
public ActionResult CreateExcelExport([FromBody]ExportRequestApiModel exportRequest)
{
try
{
var records = _mapper.Map<IEnumerable<ExportRecord>>(exportRequest.Records);
var result = _excelFileManager.GenerateFile(records, "sheet 1", 1);
return new FileStreamResult(result,
new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
{
FileDownloadName = "export.xlsx"
};
}
catch (Exception ex)
{
if (!(ex is BusinessException))
_logger?.LogError(LoggingEvents.GeneralException, ex, ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
//Action Method test.
[Fact]
public void CanCreateExportFile()
{
//Arrange
var exportRequestApiModel = new ExportRequestApiModel()
{
Records = new List<ExportRecordApiModel>() { }
};
var exportRecords = new List<ExportRecord>
{
new ExportRecord()
};
_mapper.Map<IEnumerable<ExportRecord>>(exportRequestApiModel.Records)
.Returns(exportRecords);
_excelFileManager.GenerateFile(exportRecords, "sheet 1", 1)
.Returns(new MemoryStream(){Position = 0});
//Act
var result = (ObjectResult) _controller.CreateExcelExport(exportRequestApiModel);
//Assert
Assert.Equal(StatusCodes.Status200OK, result.StatusCode);
Assert.IsType<FileStream>(result.Value);
}
Related
I am writing unit test case for my Entity Framework Core SaveAsync method.
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = _dbContext.TableName.AddAsync(dbObj);
_ = _dbContext.SaveChangesAsync(context.CancellationToken);
}
catch (Exception ex)
{
_logger.LogCritical($"{GetType().Name}:{nameof(Consume)} {ex}");
}
await Task.CompletedTask;
I have written unit test case by mocking:
Mock<MyDbContext> dbContext = new();
var data = return new List<TableName>()
{
new TableName
{
Id = 1,
Description = "test"
}
};
dbContext.Setup(r => r.TableName).ReturnsDbSet(data);
dbContext.Setup(r => r.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);
// Check that each method was only called once.
//dbContext.Verify(x => x.TableName.Add(It.IsAny<TableName>()), Times.Once());
dbContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
Below is what I get as a result
Moq.MockException : Expected invocation on the mock once, but was 0 times
While debugging the test case, I am getting the below.
Firstly,add await into try catch block.otherwise it would not catch excption
try
{
// Here add business logic to insert record in to new database
var dbObj = new TableName()
{
Code = 1,
Description = "test"
};
_ = await _dbContext.TableName.AddAsync(dbObj);
_ = await _dbContext.SaveChangesAsync(context.CancellationToken);
}
//remove this line
await Task.CompletedTask;
The document related with Asynchronous programming
And you could check the document related with testing non query scenarios
I tried as below:
Assuming a controller:
public async Task<IActionResult> Create([Bind("Id,Name")] SomeEntity someEntity)
{
if (ModelState.IsValid)
{
try
{
await _context.AddAsync(someEntity);
await _context.SaveChangesAsync();
}
catch (Exception e)
{
}
return RedirectToAction(nameof(Index));
}
return View(someEntity);
}
Test:
public class UnitTest1
{
[Fact]
public async Task Test1()
{
var mockset = new Mock<DbSet<SomeEntity>>();
var mockdbcontext = new Mock<WebApplication8Context>(new DbContextOptions<WebApplication8Context>() );
mockdbcontext.Setup(x=>x.SomeEntity).Returns(mockset.Object);
var a = mockdbcontext.Object;
var controller = new SomeEntitiesController(mockdbcontext.Object);
await controller.Create(new SomeEntity() { Name = "someName" });
mockdbcontext.Verify(x => x.SaveChangesAsync(default(CancellationToken)), Times.Once);
}
}
Result:
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.
I'm trying to write a Unit test case for the delete method of the below controller
public class AssetProxyController : Controller
{
private IRiskAssetProxyService _assetProxyService;
public AssetProxyController( IRiskAssetProxyService assetProxyService)
{
_assetProxyService = assetProxyService;
}
[HttpDelete("{assetId}")]
public ActionResult Delete(string assetId)
{
if (_assetProxyService.DeleteAssetProxyMapping(assetId))
{
return Ok("AssetProxy Deleted");
}
else
{
return BadRequest("Unable to delete AssetProxy");
}
}
}
Test
[TestMethod]
public void Delete_ShouldReturnDeleteAssetProxy()
{
//Mock
var mockContext = new Mock<MainDBContext>();
var faker = AutoFaker.Create();
var assetProxyMappings = faker.Generate<List<AssetProxyMapping>>();
var mockAssetProxyDbSet = GetQueryableMockDbSet<AssetProxyMapping>(assetProxyMappings);
mockContext.Setup(c => c.AssetProxyMapping).Returns(mockAssetProxyDbSet);
//Test
var mocklogger = new Mock<ILogger<RiskDataControllerDbAccess>>();
var positiondbaccess = new Mock<RiskAssetProxyDbAccess>(mockContext,mocklogger);
var mockserviceLogger = new Mock<ILogger<RiskAssetProxyService>>();
var positionService = new Mock<RiskAssetProxyService>(positiondbaccess, mockserviceLogger);
var positionController = new AssetProxyController(positionService.Object);
Assert.AreEqual(true, true);
}
But i keep getting the exception
Test method Risk.DataService.UnitTests.API.AssetProxyControllerTests.Delete_ShouldReturnDeleteAssetProxy threw exception:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: Risk.DataServices.RiskAssetProxyService.
Could not find a constructor that would match given arguments:
Moq.Mock`1[Risk.DataServices.RiskAssetProxyDbAccess]
Moq.Mock`1[Microsoft.Extensions.Logging.ILogger`1[Risk.DataServices.RiskAssetProxyService]]
The currently shown test is completely over-engineered based on the shown subject under test.
Given the shown Delete controller action, the test case should be simplified
[TestMethod]
public void AssetProxy_Delete_Should_Return_Ok() {
//Arrange
string assetId = "assetId";
var serviceMock = new Mock<IRiskAssetProxyService>();
serviceMock.Setup(_ => _.DeleteAssetProxyMapping(assetId))
.Returns(true);
AssetProxyController controller = new AssetProxyController(serviceMock.Object);
//Act
ActionResult result = controller.Delete(assetId);
//Assert - using fluent assertions
result.Should().NotBeNull();
string expected = "AssetProxy Deleted";
OkObjectResult okResult = result as OkObjectResult;
okResult.Should().NotBeNull();
string actual = okResult.Value as string;
actual.Should().Be(expected); //same as Assert.AreEqual(expected, actual);
}
Another test case can be done to assert bad requests and would be similar to the case shown above except that the mocked service will be arranged to return false in order to cause the expected behavior and the assertion updated accordingly
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.
So, I'm starting to learn an implement UniTesting on a Web Api project I'm working on. What's happening is that when I setup a mock a call this one returns null instead of the value I'm telling to returns. I don't know why a similiar Setup works but this one just doesn't.
Here is my Test Class
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private MaterialsController controller;
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IRTWAPIIdentityService> identityService;
List<MaterialAccepted> materials;
MaterialAccepted material;
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object);
material = new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
materials = new List<MaterialAccepted>()
{
new MaterialAccepted() { business=true,businessService=EnumRecycleCenterService.Dropoff,residential=false,residentialService=EnumRecycleCenterService.Pickup,note="this a note"},
new MaterialAccepted() { business=false,businessService=EnumRecycleCenterService.Dropoff,residential=true,residentialService=EnumRecycleCenterService.Pickup,note="this a note"},
};
}
[TestMethod]
public void Post_ShouldReturnBadRequestWhenMaterialAcceptedModelValidationFails()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
controller.ModelState.AddModelError("error", "unit test error");
//act
var actionResult = controller.Post(2, new MaterialAcceptedModel());
Assert.IsInstanceOfType(actionResult, typeof(BadRequestResult));
}
}
}
Here is the action in the Controller I'm trying to test
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
If I run this test it will fail because it returns an instancy type of NotFoundResult instead of a BadRequestResult, and this is happening because the test method stops in this line
if (recycleCenter == null)
return NotFound();
But this test it suppose to stop on this line
if (!ModelState.IsValid)
return BadRequest(ModelState);
Any ideas why this
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
is returning null when it should return a new RecycleCenter
It seems like you are setting up the repository mock for rcid = 3, and calling the repository in the controller with rcid = 2.
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
controller.ModelState.AddModelError("error", "unit test error");
//act
var actionResult = controller.Post(2, new MaterialAcceptedModel());
Try calling it with rcid = 3
var actionResult = controller.Post(3, new MaterialAcceptedModel());
or change the Moq setup parameter to It.IsAny<int>()