Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Autorstwa Steve'a Smitha
Testy jednostkowe obejmują testowanie części aplikacji w izolacji od jej infrastruktury i zależności. Podczas testowania jednostkowego logiki kontrolera sprawdzana jest tylko zawartość pojedynczej akcji, a nie zachowanie jego zależności ani samego frameworka.
Kontrolery testów jednostkowych
Skonfiguruj testy jednostkowe akcji kontrolera, koncentrując się na zachowaniu kontrolera. Test jednostkowy kontrolera unika scenariuszy takich jak filtry, routing i wiązanie modelu. Testy obejmujące interakcje między składnikami, które wspólnie odpowiadają na żądanie, są obsługiwane przez testy integracji. Aby uzyskać więcej informacji na temat testów integracji, zobacz Testy integracji w programie ASP.NET Core.
Jeśli piszesz niestandardowe filtry i routing, przetestuj je w izolacji, a nie w ramach testów dla określonej akcji kontrolera.
Aby zademonstrować testy jednostkowe kontrolera, zapoznaj się z poniższym kontrolerem w przykładowej aplikacji.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Kontroler Home wyświetla listę sesji burzy mózgów i umożliwia tworzenie nowych sesji burzy mózgów z żądaniem POST:
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));
}
}
Poprzedni kontroler:
- Przestrzega Zasady jawnych zależności.
- Oczekuje, że iniekcja zależności (DI) dostarczy instancję klasy
IBrainstormSessionRepository. - Można to przetestować przy użyciu pozorowanej usługi
IBrainstormSessionRepositoryoraz frameworka obiektów pozorowanych, takiego jak Moq. Obiekt mockowany jest obiektem skonstruowanym ze wstępnie określonym zestawem zachowań właściwości i metod używanych do testowania. Aby uzyskać więcej informacji, zobacz Wprowadzenie do testów integracji.
Metoda HTTP GET Index nie ma pętli ani rozgałęziania i wywołuje tylko jedną metodę. Test jednostkowy dla tej akcji:
- Imituje usługę
IBrainstormSessionRepositoryprzy użyciu metodyGetTestSessions.GetTestSessionsTworzy dwie próbne sesje burzy mózgów z datami i nazwami sesji. - Wykonuje metodę
Index. - Wykonuje asercje na wyniku zwróconym przez metodę:
- Zwracane jest A ViewResult.
- Element ViewDataDictionary.Model to
StormSessionViewModel. - Istnieją dwie sesje burzy mózgów przechowywane w
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;
}
Test metody kontrolera HomeHTTP POST Index sprawdza, czy:
- Gdy parametr ModelState.IsValid to
false, metoda akcji zwraca 400 Nieprawidłowe żądanieViewResult z odpowiednimi danymi. - Gdy
ModelState.IsValidma wartośćtrue:- Wywoływana jest metoda
Addrepozytorium. - RedirectToActionResult jest zwracany z poprawnymi argumentami.
- Wywoływana jest metoda
Nieprawidłowy stan modelu testuje się, dodając błędy przy użyciu AddModelError, jak pokazano w pierwszym teście poniżej:
[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();
}
Jeśli element ModelState jest niepoprawny, zwracany jest ten sam ViewResult, co w przypadku żądania GET. Test nie próbuje przejść w nieprawidłowym modelu. Przekazywanie nieprawidłowego modelu nie jest prawidłowym podejściem, ponieważ powiązanie modelu nie jest uruchomione (chociaż test integracji używa powiązania modelu). W takim przypadku powiązanie modelu nie jest testowane. Te testy jednostkowe testują tylko kod w metodzie akcji.
Drugi test sprawdza, że jeśli element ModelState jest prawidłowy:
- Nowy
BrainstormSessionjest dodawany (za pośrednictwem repozytorium). - Metoda zwraca
RedirectToActionResultobiekt z oczekiwanymi właściwościami.
Wyśmiewane wywołania, które nie są wywoływane, są zwykle ignorowane, ale wywołanie Verifiable na końcu wywołania konfiguracji umożliwia pozorowanie weryfikacji w teście. Odbywa się to przez wywołanie mockRepo.Verify, co powoduje niepowodzenie testu, jeśli oczekiwana metoda nie została wywołana.
Note
Biblioteka Moq używana w tym przykładzie umożliwia mieszanie weryfikowalnych lub "surowych" makiety z niemożliwymi do zweryfikowania makiety (nazywane również "luźnymi" makietami lub wycinkami). Dowiedz się więcej o dostosowywaniu zachowania obiektu Mock w bibliotece Moq.
SessionController w przykładowej aplikacji wyświetla informacje związane z określoną sesją burzy mózgów. Kontroler zawiera logikę do obsługi nieprawidłowych id wartości (istnieją dwa return scenariusze w poniższym przykładzie, aby uwzględnić te scenariusze). Ostatnia instrukcja return zwraca nowy StormSessionViewModel do widoku (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);
}
}
Testy jednostkowe obejmują jeden test dla każdego return scenariusza w akcji Index kontrolera Session:
[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);
}
Przechodząc do kontrolera Ideas, aplikacja udostępnia funkcje jako internetowy interfejs API pod ścieżką api/ideas:
- Lista pomysłów (
IdeaDTO) powiązanych z sesją burzy mózgów jest zwracana przez metodęForSession. - Metoda
Createdodaje nowe pomysły do sesji.
[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);
}
Unikaj zwracania encji domeny biznesowej bezpośrednio za pośrednictwem wywołań interfejsu API. Encje domenowe:
- Często zawierają więcej danych niż wymaga klient.
- Niepotrzebnie połącz wewnętrzny model domeny aplikacji z publicznie uwidocznionym interfejsem API.
Mapowanie między jednostkami domeny a typami zwracanym do klienta można wykonać:
- Ręcznie za pomocą linQ
Select, jak w przykładowej aplikacji. Aby uzyskać więcej informacji, zobacz LINQ (Language Integrated Query). - Automatycznie z biblioteką, taką jak AutoMapper.
Następnie przykładowa aplikacja demonstruje testy jednostkowe metod Create i ForSession API kontrolera Ideas.
Przykładowa aplikacja zawiera dwa ForSession testy. Pierwszy test określa, czy ForSession zwraca wartość NotFoundObjectResult (nie znaleziono protokołu HTTP) dla nieprawidłowej sesji:
[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);
}
Drugi ForSession test sprawdza, czy ForSession zwraca listę propozycji sesji (<List<IdeaDTO>>) dla poprawnej sesji. Kontrole sprawdzają również pierwszą koncepcję, aby potwierdzić, że jej właściwość Name jest poprawna:
[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);
}
Aby przetestować działanie metody Create, gdy parametr ModelState jest nieprawidłowy, przykładowa aplikacja dodaje do kontrolera błąd modelu w ramach testowania. Nie próbuj testować walidacji modelu ani powiązania modelu w testach jednostkowych — po prostu przetestuj zachowanie metody akcji w obliczu nieprawidłowego ModelState:
[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);
}
Drugi test Create funkcji zależy od zwracanego nullrepozytorium , więc repozytorium makietowe jest skonfigurowane tak, aby zwracało wartość null. Nie ma potrzeby tworzenia testowej bazy danych (w pamięci lub w inny sposób) i tworzenia zapytania zwracającego ten wynik. Test można wykonać w jednej instrukcji, jak pokazano w przykładowym kodzie:
[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);
}
Trzeci Create test sprawdza, Create_ReturnsNewlyCreatedIdeaForSessionczy metoda repozytorium jest wywoływana UpdateAsync . Pozorowana metoda jest wywoływana za pomocą Verifiablemetody , a metoda wyśmiewanego repozytorium Verify jest wywoływana w celu potwierdzenia wykonania metody weryfikowalnej. Nie jest to odpowiedzialność za test jednostkowy w celu zapewnienia, że UpdateAsync metoda zapisała dane — które można wykonać z testem integracji.
[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>) może zwrócić typ pochodzący z ActionResult lub zwrócić określony typ.
Przykładowa aplikacja zawiera metodę, która dla danej sesji id zwraca List<IdeaDTO>. Jeśli sesja id nie istnieje, kontroler zwraca NotFound:
[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;
}
Dwa testy kontrolera ForSessionActionResult są zawarte w ApiIdeasControllerTests.
Pierwszy test potwierdza, że kontroler zwraca ActionResult, ale nie nieistniejącą listę pomysłów na nieistniejącą sesję id:
- Typ
ActionResultjestActionResult<List<IdeaDTO>>. - Element Result to 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);
}
W przypadku prawidłowej sesji iddrugi test potwierdza, że metoda zwraca następujące elementy:
- Element
ActionResulttypuList<IdeaDTO>. - Element ActionResult<T>.Value jest typu
List<IdeaDTO>. - Pierwszy element na liście jest prawidłową ideą zgodną z ideą przechowywaną w sesji mock (uzyskaną przez wywołanie metody
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);
}
Przykładowa aplikacja zawiera również metodę tworzenia nowej Idea dla danej sesji. Kontroler zwraca:
- BadRequest dla nieobsługiwanego modelu.
- NotFound jeśli sesja nie istnieje.
- CreatedAtAction gdy sesja zostanie zaktualizowana o nowy pomysł.
[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);
}
Trzy testy CreateActionResult są zawarte w ApiIdeasControllerTests.
Pierwszy tekst potwierdza, że zwracany jest element BadRequest dla nieprawidłowego modelu.
[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);
}
Drugi test sprawdza, czy zwracany jest NotFound, jeśli sesja nie istnieje.
[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);
}
Dla prawidłowej sesji id test końcowy potwierdza, że:
- Metoda zwraca
ActionResulttypuBrainstormSession. -
ActionResult<T>.Result to CreatedAtActionResult.
CreatedAtActionResultjest analogiczny do odpowiedzi 201 Utworzono z nagłówkiemLocation. -
ActionResult<T>.Value to typ
BrainstormSession. - Wywołanie pozorne w celu zaktualizowania sesji,
UpdateAsync(testSession)zostało wywołane. WywołanieVerifiablemetody jest sprawdzane przez wykonaniemockRepo.Verify()w asercji. - Dla sesji są zwracane dwa
Ideaobiekty. - Ostatni element (
Ideadodany przez pozorowane wywołanie doUpdateAsync) odpowiada elementowinewIdeadodanemu do sesji w teście.
[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);
}
Kontrolery odgrywają kluczową rolę w dowolnej aplikacji MVC platformy ASP.NET Core. W związku z tym należy mieć pewność, że kontrolery zachowują się zgodnie z oczekiwaniami. Testy automatyczne mogą wykrywać błędy przed wdrożeniem aplikacji w środowisku produkcyjnym.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Testy jednostkowe logiki kontrolera
Testy jednostkowe obejmują testowanie części aplikacji w izolacji od jej infrastruktury i zależności. Podczas testowania jednostkowego logiki kontrolera sprawdzana jest tylko zawartość pojedynczej akcji, a nie zachowanie jego zależności ani samego frameworka.
Skonfiguruj testy jednostkowe akcji kontrolera tak, aby skupiały się na zachowaniu kontrolera. Test jednostkowy kontrolera pomija scenariusze takie jak filtry, routing i wiązanie modelu. Testy obejmujące interakcje między składnikami, które wspólnie odpowiadają na żądanie, są obsługiwane przez testy integracyjne. Aby uzyskać więcej informacji na temat testów integracyjnych, zobacz Testy integracyjne w ASP.NET Core.
Jeśli piszesz niestandardowe filtry i routing, testuj je jednostkowo w izolacji, a nie jako część testów konkretnej akcji kontrolera.
Aby zademonstrować testy jednostkowe kontrolera, zapoznaj się z następującym kontrolerem w przykładowej aplikacji. Kontroler Home wyświetla listę sesji burzy mózgów i umożliwia tworzenie nowych sesji burzy mózgów za pomocą żądania POST:
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));
}
}
Poprzedni kontroler:
- Przestrzega zasady jawnych zależności.
-
Oczekuje iniekcji zależności (DI), aby dostarczyć instancję
IBrainstormSessionRepository - Można testować z użyciem symulowanej usługi
IBrainstormSessionRepositoryoraz frameworku obiektów pozorowanych, takiego jak Moq. Obiekt pozorowany to sztucznie utworzony obiekt ze z góry określonym zestawem zachowań właściwości i metod, używany do testowania. Aby uzyskać więcej informacji, zobacz Wprowadzenie do testów integracyjnych.
Metoda HTTP GET Index nie ma pętli ani rozgałęziania i wywołuje tylko jedną metodę. Test jednostkowy dla tej akcji:
- Wyśmiewa usługę
IBrainstormSessionRepositoryGetTestSessionsprzy użyciu metody .GetTestSessionsTworzy dwie makiety sesji burzy mózgów z datami i nazwami sesji. - Wykonuje metodę
Index. - Tworzy asercje dotyczące wyniku zwróconego przez metodę:
- Zwracane jest A ViewResult.
-
ViewDataDictionary.Model jest elementem
StormSessionViewModel. - Istnieją dwie sesje burzy mózgów przechowywane w
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;
}
Metoda HTTP POST Index kontrolera Home sprawdza, że:
- Gdy parametr ModelState.IsValid to
false, metoda akcji zwraca 400 Nieprawidłowe żądanieViewResult z odpowiednimi danymi. - Gdy
ModelState.IsValidma wartośćtrue:- Wywoływana
Addjest metoda w repozytorium. - Element A RedirectToActionResult jest zwracany z poprawnymi argumentami.
- Wywoływana
Nieprawidłowy stan modelu jest testowany przez dodanie błędów, jak AddModelError pokazano w pierwszym teście poniżej:
[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();
}
Jeśli parametr ModelState jest nieprawidłowy, jest zwracany taki sam ViewResult jak w przypadku żądania GET. Test nie próbuje przekazać nieprawidłowego modelu. Przekazywanie niepoprawnego modelu nie jest właściwym podejściem, ponieważ wiązanie modelu nie działa (chociaż test integracyjny korzysta z wiązania modelu). W takim przypadku powiązanie modelu nie jest testowane. Te testy jednostkowe testują tylko kod w metodzie obsługi akcji.
Drugi test sprawdza, czy gdy ModelState element jest prawidłowy:
- Zostanie dodany nowy
BrainstormSession(za pośrednictwem repozytorium). - Metoda zwraca
RedirectToActionResultobiekt z oczekiwanymi właściwościami.
Zamockowane wywołania, które nie zostały wywołane, są zwykle ignorowane, ale wywołanie Verifiable na końcu wywołania konfiguracji umożliwia weryfikację obiektu mock w teście. Jest to wykonywane za pomocą wywołania mockRepo.Verify, które powoduje niepowodzenie testu, jeśli oczekiwana metoda nie została wywołana.
Note
Biblioteka Moq używana w tym przykładzie umożliwia łączenie weryfikowalnych, czyli „ścisłych”, obiektów pozorowanych z nieweryfikowalnymi obiektami pozorowanymi (nazywanymi również „luźnymi” obiektami pozorowanymi lub zaślepkami). Dowiedz się więcej na temat dostosowywania zachowania makiety za pomocą narzędzia Moq.
SessionController w przykładowej aplikacji wyświetla informacje związane z określoną sesją burzy mózgów. Kontroler zawiera logikę do obsługi nieprawidłowych id wartości (istnieją dwa return scenariusze w poniższym przykładzie, aby uwzględnić te scenariusze).
return Końcowa instrukcja zwraca nowy StormSessionViewModel widok (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);
}
}
Testy jednostkowe obejmują jeden test dla każdego return scenariusza w akcji Kontroler Index sesji:
[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);
}
Przejście do kontrolera Ideas umożliwia aplikacji uwidacznianie funkcji jako internetowego interfejsu API na api/ideas trasie:
- Lista pomysłów (
IdeaDTO) skojarzonych z sesją burzy mózgów jest zwracana przez metodęForSession. - Metoda
Createdodaje nowe pomysły do sesji.
[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);
}
Unikaj zwracania jednostek domeny biznesowej bezpośrednio za pośrednictwem wywołań interfejsu API. Jednostki domeny:
- Często zawierają więcej danych niż wymaga klient.
- Niepotrzebnie połącz wewnętrzny model domeny aplikacji z publicznie udostępnionym interfejsem API.
Mapowanie między jednostkami domeny a typami zwracanym do klienta można wykonać:
- Ręcznie za pomocą linQ
Select, jak używa przykładowa aplikacja. Aby uzyskać więcej informacji, zobacz LINQ (Zapytanie zintegrowane w języku). - Automatycznie z biblioteką, taką jak AutoMapper.
Następnie przykładowa aplikacja demonstruje testy jednostkowe metod Create i ForSession interfejsu API kontrolera Ideas.
Przykładowa aplikacja zawiera dwa ForSession testy. Pierwszy test określa, czy ForSession zwraca wartość NotFoundObjectResult (HTTP Not Found) dla nieprawidłowej sesji:
[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);
}
Drugi ForSession test określa, czy ForSession zwraca listę pomysłów sesji (<List<IdeaDTO>>) dla prawidłowej sesji. Kontrole sprawdzają również pierwszy pomysł, aby potwierdzić, że jego Name właściwość jest poprawna:
[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);
}
Aby przetestować zachowanie Create metody, gdy ModelState parametr jest nieprawidłowy, przykładowa aplikacja dodaje błąd modelu do kontrolera w ramach testu. Nie próbuj testować walidacji modelu ani powiązania modelu w testach jednostkowych — po prostu przetestuj zachowanie metody akcji, gdy otrzyma nieprawidłowy ModelState:
[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);
}
Drugi test elementu Create zależy od tego, czy repozytorium zwraca null, więc pozorowane repozytorium skonfigurowano tak, aby zwracało null. Nie ma potrzeby tworzenia testowej bazy danych (w pamięci lub w inny sposób) i konstruowania zapytania zwracającego ten wynik. Test można wykonać w jednej instrukcji, jak pokazano w przykładowym kodzie:
[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);
}
Trzeci Create test sprawdza, Create_ReturnsNewlyCreatedIdeaForSessionczy metoda repozytorium jest wywoływana UpdateAsync . Obiekt mock jest wywoływany z użyciem Verifiable, a metoda Verify mockowanego repozytorium jest wywoływana w celu potwierdzenia wykonania metody weryfikowalnej. Nie jest obowiązkiem testu jednostkowego, aby upewnić się, że metoda UpdateAsync zapisała dane — można to sprawdzić testem integracyjnym.
[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>
W systemie ASP.NET Core 2.1 lub nowszym ActionResult<T> (ActionResult<TValue>) umożliwia zwrócenie typu pochodzącego z ActionResultActionResult lub zwrócenie określonego typu.
Przykładowa aplikacja zawiera metodę zwracającą List<IdeaDTO> dla danej sesji id. Jeśli sesja id nie istnieje, kontroler zwraca NotFound:
[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;
}
Dwa testy kontrolera ForSessionActionResult są zawarte w ApiIdeasControllerTests.
Pierwszy test potwierdza, że kontroler zwraca element ActionResult, ale nie nieistniejącą listę pomysłów na nieistniejącą sesję id:
- Typ
ActionResultjestActionResult<List<IdeaDTO>>. - Ten Result jest 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);
}
W przypadku prawidłowej sesji id, drugi test potwierdza, że metoda zwraca:
- Element typu
List<IdeaDTO>zActionResult. - AkcjaResult <T>. Wartość jest typem
List<IdeaDTO>. - Pierwszy element na liście jest prawidłową ideą zgodną z ideą przechowywaną w sesji mock (uzyskaną przez wywołanie metody
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);
}
Przykładowa aplikacja zawiera również metodę służącą do tworzenia nowej Idea dla danej sesji. Kontroler zwraca następujące dane:
- BadRequest dla nieprawidłowego modelu.
- NotFound jeśli sesja nie istnieje.
- CreatedAtAction gdy sesja zostanie zaktualizowana o nowy pomysł.
[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);
}
Trzy testy CreateActionResult są zawarte w ApiIdeasControllerTests.
Pierwszy tekst potwierdza, że BadRequest element jest zwracany dla nieprawidłowego modelu.
[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);
}
Drugi test sprawdza, czy NotFound jest zwracany, jeśli sesja nie istnieje.
[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);
}
W przypadku prawidłowej sesji idkońcowy test potwierdza, że:
- Metoda zwraca
ActionResulttypuBrainstormSession. -
ActionResult<T>.Result to CreatedAtActionResult.
CreatedAtActionResultjest analogiczny do odpowiedzi 201 Utworzono z nagłówkiemLocation. - AkcjaResult <T>. Wartość jest typem
BrainstormSession. - Pozorowane wywołanie w celu zaktualizowania sesji,
UpdateAsync(testSession), zostało wykonane. WywołanieVerifiablemetody jest sprawdzane przez wykonaniemockRepo.Verify()w asercji. - Dla sesji są zwracane dwa
Ideaobiekty. - Ostatni element (
Ideadodany przez wywołanie makiety doUpdateAsync) jest zgodny z elementemnewIdeadodanym do sesji w teście.
[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);
}
Treści powiązane
- Testy integracyjne w ASP.NET Core
- Tworzenie i uruchamianie testów jednostkowych za pomocą programu Visual Studio
- MyTested.AspNetCore.Mvc — biblioteka do płynnego testowania dla ASP.NET Core MVC: biblioteka do testów jednostkowych z silnym typowaniem, udostępniająca płynny interfejs do testowania aplikacji MVC i interfejsów API sieci Web. (Nie jest utrzymywane ani obsługiwane przez firmę Microsoft.)
- JustMockLite: framework do mockowania dla programistów .NET. (Nie jest utrzymywane ani obsługiwane przez firmę Microsoft.)