Megosztás a következőn keresztül:


Egységteszt-vezérlő logikája a ASP.NET Core-ban

Készítette: Steve Smith

Az egységtesztek magukban foglalják az alkalmazások egy részének tesztelését az infrastruktúrától és a függőségektől elkülönítve. Az egységtesztelési vezérlő logikája esetén a rendszer csak egyetlen művelet tartalmát teszteli, nem pedig a függőségek vagy maga a keretrendszer viselkedését.

Egységtesztelési vezérlők

Állítsa be a vezérlőműveletek egységtesztjeit, hogy a vezérlő viselkedésére összpontosítson. A vezérlőegység-teszt elkerüli az olyan forgatókönyveket, mint a szűrők, az útválasztás és a modellkötés. A kérelmekre együttesen válaszoló összetevők közötti interakciókat lefedő teszteket integrációs tesztek kezelik. Az integrációs tesztekkel kapcsolatos további információkért lásd az integrációs teszteket a ASP.NET Core-ban.

Ha egyéni szűrőket és útvonalakat ír, az egységek külön tesztelik őket, nem pedig egy adott vezérlőművelet tesztjeinek részeként.

A vezérlőegység-tesztek bemutatásához tekintse át az alábbi vezérlőt a mintaalkalmazásban.

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

A Home vezérlő megjeleníti az ötletgyűjtési munkamenetek listáját, és lehetővé teszi új ötletgyűjtési munkamenetek létrehozását POST-kéréssel:

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));
    }
}

Az előző vezérlő:

A HTTP GET Index metódus nem rendelkezik hurkolással vagy elágaztatással, és csak egy metódust hív meg. A művelet egységtesztje:

  • A IBrainstormSessionRepository módszer használatával szimulálja a GetTestSessions szolgáltatást. GetTestSessions két makett ötletgyűjtési munkamenetet hoz létre dátumokkal és munkamenetnevekkel.
  • Végrehajtja a metódust Index .
  • A metódus által visszaadott eredményre vonatkozó állításokat tesz:
[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;
}

A Home vezérlő metódustesztjei HTTP POST Index ellenőrzik, hogy:

  • Ha a ModelState.IsValid értéke, falsea műveletmetódus egy 400 hibás kéréstViewResult ad vissza a megfelelő adatokkal.
  • Amikor ModelState.IsValidtrue:
    • A Add metódus megvan hívva az adattáron.
    • A RedirectToActionResult a megfelelő argumentumokkal kerül visszaadásra.

Az érvénytelen modellállapotokat az alábbi első tesztben látható hibák hozzáadásával AddModelError teszteljük:

[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();
}

Ha a ModelState érvénytelen, ugyanaz ViewResult lesz visszaadva, mint egy GET-kérés esetében. A teszt nem próbál meg érvénytelen modellt átadni. Érvénytelen modell átadása nem érvényes módszer, mivel a modellkötés nem fut (bár egy integrációs teszt modellkötést használ). Ebben az esetben a modellkötés nincs tesztelve. Ezek az egységtesztek csak a műveletmetódusban tesztelik a kódot.

A második teszt ellenőrzi, hogy amikor érvényes a ModelState,

  • Új BrainstormSession kerül hozzáadásra (az adattáron keresztül).
  • A metódus a RedirectToActionResult várt tulajdonságokat adja vissza.

A nem hívott mock hívásokat általában figyelmen kívül hagyják, de a beállítás végén végrehajtott hívás Verifiable lehetővé teszi a mock érvényesítését a tesztben. Ez a mockRepo.Verify hívásával történik, amely megbuktatja a tesztet, ha a várt metódust nem hívják meg.

Note

Az ebben a mintában használt Moq könyvtár lehetővé teszi az ellenőrizhető vagy "szigorú" mockok és nem ellenőrizhető mockok, más néven "laza" mockok vagy csonkok, kombinációját. További információ a Mock viselkedésének a Moq használatával történő testreszabásáról.

A mintaalkalmazás SessionControllerje egy adott ötletgyűjtési munkamenettel kapcsolatos információkat jelenít meg. A vezérlő az érvénytelen id értékek kezelésére szolgáló logikát tartalmaz (az alábbi példában két return forgatókönyv foglalkozik ezekkel a forgatókönyvekkel). A végső return utasítás egy új StormSessionViewModel nézetet ad vissza (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);
    }
}

Az egységtesztek egy tesztet tartalmaznak a munkamenet-vezérlő return művelet minden Index egyes forgatókönyvéhez:

[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);
}

Az Ötletek vezérlőre lépve az alkalmazás webes API-ként teszi elérhetővé a funkciókat az api/ideas útvonalon:

  • A IdeaDTO ötletgyűjtési munkamenethez társított ötletek listáját a ForSession metódus adja vissza.
  • A Create metódus új ötleteket ad hozzá egy munkamenethez.
[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);
}

Kerülje az üzleti tartományentitások közvetlen visszaadását API-hívásokon keresztül. Tartományi entitások:

  • Gyakran több adatot tartalmaz, mint amennyit az ügyfél igényel.
  • Szükségtelenül párosítja az alkalmazás belső tartománymodelljét a nyilvánosan közzétett API-val.

A tartományentitások és az ügyfélnek visszaadott típusok közötti megfeleltetés elvégezhető:

A mintaalkalmazás ezután az Ötletek vezérlő API-módszereként a Create és ForSession egységteszteket demonstrálja.

A mintaalkalmazás két ForSession tesztet tartalmaz. Az első teszt azt vizsgálja, hogy ForSession egy érvénytelen munkamenet esetén NotFoundObjectResult-t (HTTP Nem található) ad-e vissza.

[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);
}

A második ForSession teszt megállapítja, hogy a munkamenet-ötletek (ForSession) listáját adja-e <List<IdeaDTO>> vissza egy érvényes munkamenethez. Az ellenőrzések azt vizsgálják, hogy az első gondolat Name tulajdonsága helyes-e.

[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);
}

A metódus viselkedésének Create teszteléséhez, ha az ModelState érvénytelen, a mintaalkalmazás modellhibát ad hozzá a vezérlőhöz a teszt részeként. Ne próbálja tesztelni a modellérvényesítést vagy a modellkötést az egységtesztekben – csak az akciómetódus viselkedését tesztelje, amikor érvénytelen ModelState-vel kerül szembe.

[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);
}

A második teszt attól függ, hogy az adattár visszaad-e Create, ezért az ál-adattár úgy van konfigurálva, hogy null-t adjon vissza. Nincs szükség tesztadatbázis létrehozására (a memóriában vagy más módon), és olyan lekérdezést létrehozni, amely ezt az eredményt adja vissza. A teszt egyetlen utasításban is elvégezhető, ahogy a mintakód is szemlélteti:

[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);
}

A harmadik Create teszt ellenőrzi, Create_ReturnsNewlyCreatedIdeaForSessionhogy az adattár metódusa UpdateAsync meghívva van-e. A makett Verifiable-val van meghívva, és a szimulált adattár Verify metódusa kerül meghívásra, hogy megerősítse az ellenőrizhető metódus végrehajtását. Nem az egységteszt feladata annak biztosítása, hogy a UpdateAsync metódus mentette az adatokat– ez egy integrációs teszttel végrehajtható.

[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);
}

ActionResult<T> tesztelése

ActionResult<T> (ActionResult<TValue>) visszaadhat egy ActionResult-ből származtatott típust vagy egy konkrét típust.

A mintaalkalmazás tartalmaz egy metódust, amely egy adott munkamenethez List<IdeaDTO>ad vissza egy id értéket. Ha a munkamenet id nem létezik, a vezérlő a következőt adja NotFoundvissza:

[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;
}

A vezérlő két tesztje ForSessionActionResult szerepel a ApiIdeasControllerTests.

Az első teszt megerősíti, hogy a vezérlő visszaad egy ActionResult, de nem egy nem létező ötletlistát egy nem létező munkamenethez id.

[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);
}

Érvényes munkamenet idesetén a második teszt megerősíti, hogy a metódus a következőt adja vissza:

  • Egy ActionResult a List<IdeaDTO> típussal.
  • Az ActionResultT.Value érték egy típus.
  • A lista első eleme egy érvényes ötlet, amely megfelel a mintamunkamenetben tárolt ötletnek (hívással GetTestSessionszerezhető be).
[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);
}

A mintaalkalmazás egy adott munkamenethez új Idea létrehozására szolgáló módszert is tartalmaz. A vezérlő a következőt adja vissza:

[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);
}

Három teszt CreateActionResult található a ApiIdeasControllerTests.

Az első szöveg megerősíti, hogy a BadRequest rendszer érvénytelen modellt ad vissza.

[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);
}

A második teszt ellenőrzi, hogy egy NotFound kerül visszaadásra, ha a munkamenet nem létezik.

[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);
}

Érvényes munkamenet idesetén az utolsó teszt megerősíti, hogy:

  • A metódus egy ActionResult típusú BrainstormSession-et ad vissza.
  • Az ActionResultT.Result egy . CreatedAtActionResult hasonló a 201 Created válaszhoz egy Location fejléccel.
  • Az ActionResultT.Value érték egy típus.
  • Meghívták a munkamenet UpdateAsync(testSession)frissítésére irányuló mintahívást. A Verifiable metódushívás ellenőrzése mockRepo.Verify() végrehajtásával történik az állításokban.
  • A rendszer két Idea objektumot ad vissza a munkamenethez.
  • Az utolsó elem (a Idea modellhívás UpdateAsyncáltal hozzáadott) megegyezik a newIdea teszt munkamenetéhez hozzáadott elemével.
[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);
}

A vezérlők központi szerepet játszanak minden ASP.NET Core MVC-alkalmazásban. Ezért biztosnak kell lennie abban, hogy a vezérlők a kívánt módon viselkednek. Az automatizált tesztek az alkalmazás éles környezetben való üzembe helyezése előtt észlelhetik a hibákat.

Mintakód megtekintése vagy letöltése (hogyan töltsd le)

Vezérlőlogika egységtesztjei

Az egységtesztek magukban foglalják az alkalmazások egy részének tesztelését az infrastruktúrától és a függőségektől elkülönítve. Az egységtesztelési vezérlő logikája esetén a rendszer csak egyetlen művelet tartalmát teszteli, nem pedig a függőségek vagy maga a keretrendszer viselkedését.

Állítsa be a vezérlőműveletek egységtesztjeit, hogy a vezérlő viselkedésére összpontosítson. A vezérlőegység-teszt elkerüli az olyan forgatókönyveket, mint a szűrők, az útválasztás és a modellkötés. A kérelmekre együttesen válaszoló összetevők közötti interakciókat lefedő teszteket integrációs tesztek kezelik. Az integrációs tesztekkel kapcsolatos további információkért lásd az integrációs teszteket a ASP.NET Core-ban.

Ha egyéni szűrőket és útvonalakat ír, az egységek külön tesztelik őket, nem pedig egy adott vezérlőművelet tesztjeinek részeként.

A vezérlőegység-tesztek bemutatásához tekintse át az alábbi vezérlőt a mintaalkalmazásban. A Home vezérlő megjeleníti az ötletgyűjtési munkamenetek listáját, és lehetővé teszi új ötletgyűjtési munkamenetek létrehozását POST-kéréssel:

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));
    }
}

Az előző vezérlő:

A HTTP GET Index metódus nem rendelkezik hurkolással vagy elágaztatással, és csak egy metódust hív meg. A művelet egységtesztje:

  • A IBrainstormSessionRepository módszer használatával szimulálja a GetTestSessions szolgáltatást. GetTestSessions két makett ötletgyűjtési munkamenetet hoz létre dátumokkal és munkamenetnevekkel.
  • Végrehajtja a metódust Index .
  • A metódus által visszaadott eredményre vonatkozó állításokat tesz:
[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;
}

A Home vezérlő metódustesztjei HTTP POST Index ellenőrzik, hogy:

  • Ha a ModelState.IsValid értéke, falsea műveletmetódus egy 400 hibás kéréstViewResult ad vissza a megfelelő adatokkal.
  • Amikor ModelState.IsValidtrue:
    • A Add metódus megvan hívva az adattáron.
    • A RedirectToActionResult a megfelelő argumentumokkal kerül visszaadásra.

Az érvénytelen modellállapotokat az alábbi első tesztben látható hibák hozzáadásával AddModelError teszteljük:

[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();
}

Ha a ModelState érvénytelen, ugyanaz ViewResult lesz visszaadva, mint egy GET-kérés esetében. A teszt nem próbál meg érvénytelen modellt átadni. Érvénytelen modell átadása nem érvényes módszer, mivel a modellkötés nem fut (bár egy integrációs teszt modellkötést használ). Ebben az esetben a modellkötés nincs tesztelve. Ezek az egységtesztek csak a műveletmetódusban tesztelik a kódot.

A második teszt ellenőrzi, hogy amikor érvényes a ModelState,

  • Új BrainstormSession kerül hozzáadásra (az adattáron keresztül).
  • A metódus a RedirectToActionResult várt tulajdonságokat adja vissza.

A nem hívott mock hívásokat általában figyelmen kívül hagyják, de a beállítás végén végrehajtott hívás Verifiable lehetővé teszi a mock érvényesítését a tesztben. Ez a mockRepo.Verify hívásával történik, amely megbuktatja a tesztet, ha a várt metódust nem hívják meg.

Note

Az ebben a mintában használt Moq könyvtár lehetővé teszi az ellenőrizhető vagy "szigorú" mockok és nem ellenőrizhető mockok, más néven "laza" mockok vagy csonkok, kombinációját. További információ a Mock viselkedésének a Moq használatával történő testreszabásáról.

A mintaalkalmazás SessionControllerje egy adott ötletgyűjtési munkamenettel kapcsolatos információkat jelenít meg. A vezérlő az érvénytelen id értékek kezelésére szolgáló logikát tartalmaz (az alábbi példában két return forgatókönyv foglalkozik ezekkel a forgatókönyvekkel). A végső return utasítás egy új StormSessionViewModel nézetet ad vissza (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);
    }
}

Az egységtesztek egy tesztet tartalmaznak a munkamenet-vezérlő return művelet minden Index egyes forgatókönyvéhez:

[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);
}

Az Ötletek vezérlőre lépve az alkalmazás webes API-ként teszi elérhetővé a funkciókat az api/ideas útvonalon:

  • A IdeaDTO ötletgyűjtési munkamenethez társított ötletek listáját a ForSession metódus adja vissza.
  • A Create metódus új ötleteket ad hozzá egy munkamenethez.
[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);
}

Kerülje az üzleti tartományentitások közvetlen visszaadását API-hívásokon keresztül. Tartományi entitások:

  • Gyakran több adatot tartalmaz, mint amennyit az ügyfél igényel.
  • Szükségtelenül párosítja az alkalmazás belső tartománymodelljét a nyilvánosan közzétett API-val.

A tartományentitások és az ügyfélnek visszaadott típusok közötti megfeleltetés elvégezhető:

A mintaalkalmazás ezután az Ötletek vezérlő API-módszereként a Create és ForSession egységteszteket demonstrálja.

A mintaalkalmazás két ForSession tesztet tartalmaz. Az első teszt azt vizsgálja, hogy ForSession egy érvénytelen munkamenet esetén NotFoundObjectResult-t (HTTP Nem található) ad-e vissza.

[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);
}

A második ForSession teszt megállapítja, hogy a munkamenet-ötletek (ForSession) listáját adja-e <List<IdeaDTO>> vissza egy érvényes munkamenethez. Az ellenőrzések azt vizsgálják, hogy az első gondolat Name tulajdonsága helyes-e.

[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);
}

A metódus viselkedésének Create teszteléséhez, ha az ModelState érvénytelen, a mintaalkalmazás modellhibát ad hozzá a vezérlőhöz a teszt részeként. Ne próbálja tesztelni a modellérvényesítést vagy a modellkötést az egységtesztekben – csak az akciómetódus viselkedését tesztelje, amikor érvénytelen ModelState-vel kerül szembe.

[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);
}

A második teszt attól függ, hogy az adattár visszaad-e Create, ezért az ál-adattár úgy van konfigurálva, hogy null-t adjon vissza. Nincs szükség tesztadatbázis létrehozására (a memóriában vagy más módon), és olyan lekérdezést létrehozni, amely ezt az eredményt adja vissza. A teszt egyetlen utasításban is elvégezhető, ahogy a mintakód is szemlélteti:

[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);
}

A harmadik Create teszt ellenőrzi, Create_ReturnsNewlyCreatedIdeaForSessionhogy az adattár metódusa UpdateAsync meghívva van-e. A makett Verifiable-val van meghívva, és a szimulált adattár Verify metódusa kerül meghívásra, hogy megerősítse az ellenőrizhető metódus végrehajtását. Nem az egységteszt feladata annak biztosítása, hogy a UpdateAsync metódus mentette az adatokat– ez egy integrációs teszttel végrehajtható.

[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);
}

ActionResult<T> tesztelése

hu-HU: Az ASP.NET Core 2.1-es vagy újabb verziójában az ActionResult<T> (ActionResult<TValue>) lehetővé teszi, hogy visszaadjon egy olyan típust, amely ActionResult-ből származik, vagy egy konkrét típust adjon vissza.

A mintaalkalmazás tartalmaz egy metódust, amely egy adott munkamenethez List<IdeaDTO>ad vissza egy id értéket. Ha a munkamenet id nem létezik, a vezérlő a következőt adja NotFoundvissza:

[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;
}

A vezérlő két tesztje ForSessionActionResult szerepel a ApiIdeasControllerTests.

Az első teszt megerősíti, hogy a vezérlő visszaad egy ActionResult, de nem egy nem létező ötletlistát egy nem létező munkamenethez id.

[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);
}

Érvényes munkamenet idesetén a második teszt megerősíti, hogy a metódus a következőt adja vissza:

  • Egy ActionResult a List<IdeaDTO> típussal.
  • Az ActionResultT.Value érték egy típus.
  • A lista első eleme egy érvényes ötlet, amely megfelel a mintamunkamenetben tárolt ötletnek (hívással GetTestSessionszerezhető be).
[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);
}

A mintaalkalmazás egy adott munkamenethez új Idea létrehozására szolgáló módszert is tartalmaz. A vezérlő a következőt adja vissza:

[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);
}

Három teszt CreateActionResult található a ApiIdeasControllerTests.

Az első szöveg megerősíti, hogy a BadRequest rendszer érvénytelen modellt ad vissza.

[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);
}

A második teszt ellenőrzi, hogy egy NotFound kerül visszaadásra, ha a munkamenet nem létezik.

[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);
}

Érvényes munkamenet idesetén az utolsó teszt megerősíti, hogy:

  • A metódus egy ActionResult típusú BrainstormSession-et ad vissza.
  • Az ActionResultT.Result egy . CreatedAtActionResult hasonló a 201 Created válaszhoz egy Location fejléccel.
  • Az ActionResultT.Value érték egy típus.
  • Meghívták a munkamenet UpdateAsync(testSession)frissítésére irányuló mintahívást. A Verifiable metódushívás ellenőrzése mockRepo.Verify() végrehajtásával történik az állításokban.
  • A rendszer két Idea objektumot ad vissza a munkamenethez.
  • Az utolsó elem (a Idea modellhívás UpdateAsyncáltal hozzáadott) megegyezik a newIdea teszt munkamenetéhez hozzáadott elemével.
[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);
}

További erőforrások