Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Door Steve Smith
Eenheidstests omvatten het testen van een deel van een app in isolatie van de infrastructuur en afhankelijkheden. Wanneer eenheidstestcontrollerlogica wordt getest, wordt alleen de inhoud van één actie getest, niet het gedrag van de afhankelijkheden of van het framework zelf.
Eenheidstestcontrollers
Stel eenheidstests van controlleracties in om te focussen op het gedrag van de controller. Een controllereenheidtest vermijdt scenario's zoals filters, routering en modelbinding. Tests die betrekking hebben op de interacties tussen onderdelen die gezamenlijk reageren op een aanvraag, worden verwerkt door integratietests. Zie Integratietests in ASP.NET Core voor meer informatie over integratietests.
Als u aangepaste filters en routes schrijft, test u deze afzonderlijk, niet als onderdeel van tests voor een bepaalde controlleractie.
Bekijk de volgende controller in de voorbeeld-app om tests van controllereenheden te demonstreren.
Voorbeeldcode bekijken of downloaden (hoe download je)
De Home controller geeft een lijst met brainstormsessies weer en maakt het mogelijk om nieuwe brainstormsessies te maken met een POST-aanvraag:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
De voorgaande controller:
- Volgt het principe van expliciete afhankelijkheden.
- Verwacht dat afhankelijkheidsinjectie (DI) een exemplaar van
IBrainstormSessionRepository. - Kan worden getest met een gesimuleerde
IBrainstormSessionRepositoryservice met behulp van een gesimuleerd objectframework, zoals Moq. Een gesimuleerd object is een ge fabriceerd object met een vooraf bepaalde set eigenschappen en methodegedrag dat wordt gebruikt voor het testen. Zie Inleiding tot integratietests voor meer informatie.
De HTTP GET Index methode heeft geen lus of vertakking en roept slechts één methode aan. De eenheidstest voor deze handeling:
- Hiermee wordt de
IBrainstormSessionRepositoryservice gesimuleerd met behulp van deGetTestSessionsmethode.GetTestSessionsmaakt twee mock brainstormsessies met datums en sessienamen. - Hiermee wordt de
Indexmethode uitgevoerd. - Doet beweringen over het resultaat dat door de methode wordt geretourneerd:
- Er wordt een ViewResult geretourneerd.
- Het ViewDataDictionary.Model is een
StormSessionViewModel. - Er zijn twee brainstormsessies opgeslagen in de
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
De Home methode van de HTTP POST Index controller test en verifieert dat:
- Wanneer ModelState.IsValid is
false, retourneert de actiemethode een 400 Ongeldige aanvraagViewResult met de juiste gegevens. - Wanneer
ModelState.IsValidistrue:- De
Addmethode in de opslagplaats wordt aangeroepen. - A RedirectToActionResult wordt geretourneerd met de juiste argumenten.
- De
Er wordt een ongeldige modelstatus getest door fouten toe te voegen met behulp van AddModelError, zoals weergegeven in de eerste test hieronder:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Als ModelState niet geldig is, wordt hetzelfde ViewResult geretourneerd als voor een GET-aanvraag. De test probeert geen ongeldig model door te geven. Het doorgeven van een ongeldig model is geen geldige benadering, omdat modelbinding niet wordt uitgevoerd (hoewel een integratietest wel modelbinding gebruikt). In dit geval wordt modelbinding niet getest. Deze eenheidstests testen alleen de code in de actiemethode.
De tweede test controleert of wanneer het ModelState geldig is:
- Er wordt een nieuwe
BrainstormSessiontoegevoegd (via de opslagplaats). - De methode retourneert een
RedirectToActionResultmet de verwachte eigenschappen.
Gesimuleerde aanroepen die niet worden aangeroepen, worden normaal gesproken genegeerd, maar het aanroepen Verifiable aan het einde van de installatieoproep maakt mockvalidatie in de test mogelijk. Dit wordt uitgevoerd met de aanroep naar mockRepo.Verify, waardoor de test mislukt als de verwachte methode niet is aangeroepen.
Note
De Moq-bibliotheek die in dit voorbeeld wordt gebruikt, maakt het mogelijk om verifieerbare of 'strikte' mocks te combineren met niet-verifieerbare mocks (ook wel 'losse' mocks of stubs genoemd). Meer informatie over het aanpassen van mockgedrag met Moq.
SessionController in de voorbeeld-app geeft informatie weer met betrekking tot een bepaalde brainstormsessie. De controller bevat logica voor het verwerken van ongeldige id waarden (er zijn twee return scenario's in het volgende voorbeeld om deze scenario's te behandelen). De laatste return instructie retourneert een nieuw StormSessionViewModel object voor de weergave (Controllers/SessionController.cs):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
De eenheidstests bevatten één test voor elk return scenario in de sessiecontrolleractie Index :
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Als u overstapt op de Ideeën-controller, wordt de functionaliteit van de app beschikbaar gesteld als een web-API op de api/ideas route:
- Een lijst met ideeën (
IdeaDTO) die zijn gekoppeld aan een brainstormsessie, wordt geretourneerd door deForSessionmethode. - Met de
Createmethode worden nieuwe ideeën toegevoegd aan een sessie.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Vermijd het rechtstreeks retourneren van zakelijke domeinentiteiten via API-aanroepen. Domeinentiteiten:
- Neem vaak meer gegevens op dan de client vereist.
- Koppel het interne domeinmodel van de app onnodig aan de openbaar weergegeven API.
Mapping tussen domeinentiteiten en de typen die aan de client worden geretourneerd, kan worden uitgevoerd:
- Handmatig met een LINQ
Select, zoals de voorbeeld-app dat doet. Zie LINQ (Language Integrated Query) voor meer informatie. - Automatisch met een bibliotheek, zoals AutoMapper.
Vervolgens demonstreert de voorbeeld-app eenheidstests voor de Create en ForSession API-methoden van de Ideeën-controller.
De voorbeeld-app bevat twee ForSession tests. De eerste test bepaalt of ForSession een NotFoundObjectResult (HTTP Niet gevonden) retourneert voor een ongeldige sessie:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
De tweede ForSession test bepaalt of ForSession een lijst met sessieideeën (<List<IdeaDTO>>) voor een geldige sessie wordt geretourneerd. De controles onderzoeken ook het eerste idee om te bevestigen dat de Name eigenschap juist is:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Als u het gedrag van de Create methode wilt testen wanneer de ModelState methode ongeldig is, voegt de voorbeeld-app een modelfout toe aan de controller als onderdeel van de test. Probeer geen modelvalidatie of modelbinding te testen in eenheidstests. Test alleen het gedrag van de actiemethode wanneer u wordt geconfronteerd met een ongeldige ModelStateactiemethode:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
De tweede test van Create hangt af van de opslagplaats die null retourneert, dus de mock-opslagplaats is geconfigureerd om null terug te geven. U hoeft geen testdatabase (in het geheugen of anderszins) te maken en een query te maken die dit resultaat retourneert. De test kan in één instructie worden uitgevoerd, zoals in de voorbeeldcode wordt geïllustreerd:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
De derde Create test controleert of de Create_ReturnsNewlyCreatedIdeaForSession methode van de repository wordt aangeroepen. De mock wordt aangeroepen met Verifiable, en de methode van de gesimuleerde opslagplaats Verify wordt aangeroepen om te bevestigen dat de verifieerbare methode wordt uitgevoerd. Het is niet de verantwoordelijkheid van de eenheidstest om ervoor te zorgen dat de UpdateAsync methode de gegevens heeft opgeslagen, die kan worden uitgevoerd met een integratietest.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Test ActionResult<T>
ActionResult<T> (ActionResult<TValue>) kan een type retourneren dat is afgeleid van ActionResult of een specifiek type retourneert.
De voorbeeld-app bevat een methode die een List<IdeaDTO> voor een bepaalde sessie idretourneert. Als de sessie id niet bestaat, retourneert NotFoundde controller:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Twee tests van de ForSessionActionResult controller zijn opgenomen in de ApiIdeasControllerTests.
De eerste test bevestigt dat de controller een ActionResult retourneert, maar geen niet-bestaande lijst met ideeën voor een niet-bestaande sessie id.
- Het
ActionResulttype isActionResult<List<IdeaDTO>>. - Het Result is een NotFoundObjectResult.
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Voor een geldige sessie idbevestigt de tweede test dat de methode retourneert:
- Een
ActionResultmet eenList<IdeaDTO>type. - De ActionResult<T>.Waarde is van het type
List<IdeaDTO>. - Het eerste item in de lijst is een geldig idee dat overeenkomt met het idee dat is opgeslagen in de mocksessie (verkregen door aan te roepen
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
De voorbeeld-app bevat ook een methode voor het maken van een nieuwe Idea voor een bepaalde sessie. De controller geeft:
- BadRequest voor een ongeldig model.
- NotFound als de sessie niet bestaat.
- CreatedAtAction wanneer de sessie wordt bijgewerkt met het nieuwe idee.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Er zijn drie tests CreateActionResult opgenomen in de ApiIdeasControllerTests.
In de eerste tekst wordt bevestigd dat er een BadRequest wordt geretourneerd voor een ongeldig model.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
De tweede test controleert of er een NotFound wordt geretourneerd als de sessie niet bestaat.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Voor een geldige sessie idbevestigt de laatste test dat:
- De methode retourneert een
ActionResultmet eenBrainstormSessiontype. - De ActionResult<T>.Result is een CreatedAtActionResult.
CreatedAtActionResultis vergelijkbaar met een 201 Gemaakt antwoord met eenLocationheader. - De ActionResult<T>.Waarde is van het type
BrainstormSession. - De mock-aanroep om de sessie bij te werken,
UpdateAsync(testSession), werd aangeroepen. DeVerifiablemethode-aanroep wordt gecontroleerd door de asserties uit temockRepo.Verify()voeren. - Er worden twee
Ideaobjecten geretourneerd voor de sessie. - Het laatste item (de
Ideatoegevoegde door de mock-aanroep)UpdateAsynckomt overeen met hetnewIdeaitem dat is toegevoegd aan de sessie in de test.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Controllers spelen een centrale rol in elke ASP.NET Core MVC-app. Daarom moet u erop vertrouwen dat controllers zich gedragen zoals bedoeld. Met geautomatiseerde tests kunnen fouten worden gedetecteerd voordat de app in een productieomgeving wordt geïmplementeerd.
Voorbeeldcode bekijken of downloaden (hoe download je)
Eenheidstests van controllerlogica
Eenheidstests omvatten het testen van een deel van een app in isolatie van de infrastructuur en afhankelijkheden. Wanneer eenheidstestcontrollerlogica wordt getest, wordt alleen de inhoud van één actie getest, niet het gedrag van de afhankelijkheden of van het framework zelf.
Stel eenheidstests van controlleracties in om te focussen op het gedrag van de controller. Een controllereenheidtest vermijdt scenario's zoals filters, routering en modelbinding. Tests die betrekking hebben op de interacties tussen onderdelen die gezamenlijk reageren op een aanvraag, worden verwerkt door integratietests. Zie Integratietests in ASP.NET Core voor meer informatie over integratietests.
Als u aangepaste filters en routes schrijft, test u deze afzonderlijk, niet als onderdeel van tests voor een bepaalde controlleractie.
Bekijk de volgende controller in de voorbeeld-app om tests van controllereenheden te demonstreren. De Home controller geeft een lijst met brainstormsessies weer en maakt het mogelijk om nieuwe brainstormsessies te maken met een POST-aanvraag:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
De voorgaande controller:
- Volgt het principe van expliciete afhankelijkheden.
- Verwacht dat afhankelijkheidsinjectie (DI) een exemplaar van
IBrainstormSessionRepository. - Kan worden getest met een gesimuleerde
IBrainstormSessionRepositoryservice met behulp van een gesimuleerd objectframework, zoals Moq. Een gesimuleerd object is een ge fabriceerd object met een vooraf bepaalde set eigenschappen en methodegedrag dat wordt gebruikt voor het testen. Zie Inleiding tot integratietests voor meer informatie.
De HTTP GET Index methode heeft geen lus of vertakking en roept slechts één methode aan. De eenheidstest voor deze handeling:
- Hiermee wordt de
IBrainstormSessionRepositoryservice gesimuleerd met behulp van deGetTestSessionsmethode.GetTestSessionsmaakt twee mock brainstormsessies met datums en sessienamen. - Hiermee wordt de
Indexmethode uitgevoerd. - Doet beweringen over het resultaat dat door de methode wordt geretourneerd:
- Er wordt een ViewResult geretourneerd.
- Het ViewDataDictionary.Model is een
StormSessionViewModel. - Er zijn twee brainstormsessies opgeslagen in de
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
De Home methode van de HTTP POST Index controller test en verifieert dat:
- Wanneer ModelState.IsValid is
false, retourneert de actiemethode een 400 Ongeldige aanvraagViewResult met de juiste gegevens. - Wanneer
ModelState.IsValidistrue:- De
Addmethode in de opslagplaats wordt aangeroepen. - A RedirectToActionResult wordt geretourneerd met de juiste argumenten.
- De
Er wordt een ongeldige modelstatus getest door fouten toe te voegen met behulp van AddModelError, zoals weergegeven in de eerste test hieronder:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Als ModelState niet geldig is, wordt hetzelfde ViewResult geretourneerd als voor een GET-aanvraag. De test probeert geen ongeldig model door te geven. Het doorgeven van een ongeldig model is geen geldige benadering, omdat modelbinding niet wordt uitgevoerd (hoewel een integratietest wel modelbinding gebruikt). In dit geval wordt modelbinding niet getest. Deze eenheidstests testen alleen de code in de actiemethode.
De tweede test controleert of wanneer het ModelState geldig is:
- Er wordt een nieuwe
BrainstormSessiontoegevoegd (via de opslagplaats). - De methode retourneert een
RedirectToActionResultmet de verwachte eigenschappen.
Gesimuleerde aanroepen die niet worden aangeroepen, worden normaal gesproken genegeerd, maar het aanroepen Verifiable aan het einde van de installatieoproep maakt mockvalidatie in de test mogelijk. Dit wordt uitgevoerd met de aanroep naar mockRepo.Verify, waardoor de test mislukt als de verwachte methode niet is aangeroepen.
Note
De Moq-bibliotheek die in dit voorbeeld wordt gebruikt, maakt het mogelijk om verifieerbare of 'strikte' mocks te combineren met niet-verifieerbare mocks (ook wel 'losse' mocks of stubs genoemd). Meer informatie over het aanpassen van mockgedrag met Moq.
SessionController in de voorbeeld-app geeft informatie weer met betrekking tot een bepaalde brainstormsessie. De controller bevat logica voor het verwerken van ongeldige id waarden (er zijn twee return scenario's in het volgende voorbeeld om deze scenario's te behandelen). De laatste return instructie retourneert een nieuw StormSessionViewModel object voor de weergave (Controllers/SessionController.cs):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
De eenheidstests bevatten één test voor elk return scenario in de sessiecontrolleractie Index :
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Als u overstapt op de Ideeën-controller, wordt de functionaliteit van de app beschikbaar gesteld als een web-API op de api/ideas route:
- Een lijst met ideeën (
IdeaDTO) die zijn gekoppeld aan een brainstormsessie, wordt geretourneerd door deForSessionmethode. - Met de
Createmethode worden nieuwe ideeën toegevoegd aan een sessie.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Vermijd het rechtstreeks retourneren van zakelijke domeinentiteiten via API-aanroepen. Domeinentiteiten:
- Neem vaak meer gegevens op dan de client vereist.
- Koppel het interne domeinmodel van de app onnodig aan de openbaar weergegeven API.
Mapping tussen domeinentiteiten en de typen die aan de client worden geretourneerd, kan worden uitgevoerd:
- Handmatig met een LINQ
Select, zoals de voorbeeld-app dat doet. Zie LINQ (Language Integrated Query) voor meer informatie. - Automatisch met een bibliotheek, zoals AutoMapper.
Vervolgens demonstreert de voorbeeld-app eenheidstests voor de Create en ForSession API-methoden van de Ideeën-controller.
De voorbeeld-app bevat twee ForSession tests. De eerste test bepaalt of ForSession een NotFoundObjectResult (HTTP Niet gevonden) retourneert voor een ongeldige sessie:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
De tweede ForSession test bepaalt of ForSession een lijst met sessieideeën (<List<IdeaDTO>>) voor een geldige sessie wordt geretourneerd. De controles onderzoeken ook het eerste idee om te bevestigen dat de Name eigenschap juist is:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Als u het gedrag van de Create methode wilt testen wanneer de ModelState methode ongeldig is, voegt de voorbeeld-app een modelfout toe aan de controller als onderdeel van de test. Probeer geen modelvalidatie of modelbinding te testen in eenheidstests. Test alleen het gedrag van de actiemethode wanneer u wordt geconfronteerd met een ongeldige ModelStateactiemethode:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
De tweede test van Create hangt af van de opslagplaats die null retourneert, dus de mock-opslagplaats is geconfigureerd om null terug te geven. U hoeft geen testdatabase (in het geheugen of anderszins) te maken en een query te maken die dit resultaat retourneert. De test kan in één instructie worden uitgevoerd, zoals in de voorbeeldcode wordt geïllustreerd:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
De derde Create test controleert of de Create_ReturnsNewlyCreatedIdeaForSession methode van de repository wordt aangeroepen. De mock wordt aangeroepen met Verifiable, en de methode van de gesimuleerde opslagplaats Verify wordt aangeroepen om te bevestigen dat de verifieerbare methode wordt uitgevoerd. Het is niet de verantwoordelijkheid van de eenheidstest om ervoor te zorgen dat de UpdateAsync methode de gegevens heeft opgeslagen, die kan worden uitgevoerd met een integratietest.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Test ActionResult<T>
In ASP.NET Core 2.1 of hoger kunt u met ActionResult<T> (ActionResult<TValue>) een type retourneren dat is afgeleid van ActionResult of een specifiek type retourneert.
De voorbeeld-app bevat een methode die een List<IdeaDTO> voor een bepaalde sessie idretourneert. Als de sessie id niet bestaat, retourneert NotFoundde controller:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Twee tests van de ForSessionActionResult controller zijn opgenomen in de ApiIdeasControllerTests.
De eerste test bevestigt dat de controller een ActionResult retourneert, maar geen niet-bestaande lijst met ideeën voor een niet-bestaande sessie id.
- Het
ActionResulttype isActionResult<List<IdeaDTO>>. - Het Result is een NotFoundObjectResult.
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Voor een geldige sessie idbevestigt de tweede test dat de methode retourneert:
- Een
ActionResultmet eenList<IdeaDTO>type. - De ActionResult<T>.Waarde is van het type
List<IdeaDTO>. - Het eerste item in de lijst is een geldig idee dat overeenkomt met het idee dat is opgeslagen in de mocksessie (verkregen door aan te roepen
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
De voorbeeld-app bevat ook een methode voor het maken van een nieuwe Idea voor een bepaalde sessie. De controller geeft:
- BadRequest voor een ongeldig model.
- NotFound als de sessie niet bestaat.
- CreatedAtAction wanneer de sessie wordt bijgewerkt met het nieuwe idee.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Er zijn drie tests CreateActionResult opgenomen in de ApiIdeasControllerTests.
In de eerste tekst wordt bevestigd dat er een BadRequest wordt geretourneerd voor een ongeldig model.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
De tweede test controleert of er een NotFound wordt geretourneerd als de sessie niet bestaat.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Voor een geldige sessie idbevestigt de laatste test dat:
- De methode retourneert een
ActionResultmet eenBrainstormSessiontype. - De ActionResult<T>.Result is een CreatedAtActionResult.
CreatedAtActionResultis vergelijkbaar met een 201 Gemaakt antwoord met eenLocationheader. - De ActionResult<T>.Waarde is van het type
BrainstormSession. - De mock-aanroep om de sessie bij te werken,
UpdateAsync(testSession), werd aangeroepen. DeVerifiablemethode-aanroep wordt gecontroleerd door de asserties uit temockRepo.Verify()voeren. - Er worden twee
Ideaobjecten geretourneerd voor de sessie. - Het laatste item (de
Ideatoegevoegde door de mock-aanroep)UpdateAsynckomt overeen met hetnewIdeaitem dat is toegevoegd aan de sessie in de test.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Aanvullende bronnen
- Integratietests in ASP.NET Core
- Eenheidstests maken en uitvoeren met Visual Studio
- MyTested.AspNetCore.Mvc - Fluent Testing Library voor ASP.NET Core MVC: Sterk getypeerde bibliotheek voor eenheidstests, die een vloeiende interface biedt voor het testen van MVC- en web-API-apps. (Niet onderhouden of ondersteund door Microsoft.)
- JustMockLite: een mocking framework voor .NET-ontwikkelaars. (Niet onderhouden of ondersteund door Microsoft.)