ASP.NET Core'da birim testi denetleyicisi mantığı

Tarafından Steve Smith

Birim testleri, bir uygulamanın altyapısından ve bağımlılıklarından yalıtılmış bir bölümünü test etmeyi içerir. Denetleyici mantığı birim testi yapılırken, bağımlılıklarının veya çerçevenin davranışı değil, yalnızca tek bir eylemin içeriği test edilir.

Denetleyicilerin birim testi

Denetleyicinin davranışına odaklanmak için denetleyici eylemleri için birim testleri oluşturun. Denetleyici birim testi filtreler, yönlendirme ve model bağlama gibi senaryolardan kaçınır. Bir isteğe toplu olarak yanıt veren bileşenler arasındaki etkileşimleri kapsayan testler, tümleştirme testleri tarafından ele alınır. Tümleştirme testleri hakkında daha fazla bilgi için bkz. ASP.NET Core'da tümleştirme testleri.

Özel filtreler ve yollar yazıyorsanız, birim testi bunları belirli bir denetleyici eylemindeki testlerin parçası olarak değil yalıtımlı olarak test edin.

Denetleyici birim testlerini göstermek için örnek uygulamada aşağıdaki denetleyiciyi gözden geçirin.

Örnek kodu görüntüleme veya indirme (nasıl indirilir)

Denetleyici Home , beyin fırtınası oturumlarının listesini görüntüler ve POST isteğiyle yeni beyin fırtınası oturumları oluşturulmasına olanak tanır:

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

Önceki denetleyici:

  • Açık Bağımlılıklar İlkesi'ni izler.
  • Bağımlılık enjeksiyonunun (DI), IBrainstormSessionRepository örneğini sağlaması beklenir.
  • Moq gibi bir sahte nesne çerçevesi kullanılarak sahte IBrainstormSessionRepository hizmetle test edilebilir. Sahte nesne, test amacıyla kullanılan, özellikleri ve yöntemleri için önceden tanımlanmış davranışlara sahip yapay olarak oluşturulmuş bir nesnedir. Daha fazla bilgi için Tümleştirme testlerine giriş konusuna bakın.

Yöntemin HTTP GET Index döngü veya dallanma yoktur ve yalnızca bir yöntemi çağırır. Bu eylem için birim testi:

  • IBrainstormSessionRepository hizmetini GetTestSessions yöntemini kullanarak taklit eder. GetTestSessions tarihler ve oturum adları içeren iki sahte beyin fırtınası oturumu oluşturur.
  • Index yöntemini yürütür.
  • Yöntemin döndürdüğü sonuç üzerinde doğrulamalar yapar:
    • Bir ViewResult döndürülür.
    • ViewDataDictionary.Model bir StormSessionViewModel'dir.
    • ViewDataDictionary.Model içinde depolanan iki beyin fırtınası oturumu bulunuyor.
[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;
}

Denetleyicinin HomeHTTP POST Index yöntem testleri şunları doğrular:

  • ModelState.IsValid olduğundafalse, eylem yöntemi uygun verilerle 400 Hatalı İstek ViewResult.
  • olduğunda ModelState.IsValidtrue:
    • Add depo nesnesindeki yöntem çağrılır.
    • doğru RedirectToActionResult bağımsız değişkenlerle döndürülür.

Geçersiz bir model durumu, aşağıdaki ilk testte gösterildiği gibi AddModelError kullanılarak hata eklenerek test edilir:

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

ModelState geçerli olmadığında, GET isteğiyle aynı ViewResult döndürülür. Test geçersiz bir model geçirmeyi denemez. Model bağlama çalışmadığından, geçersiz bir model iletmek geçerli bir yaklaşım değildir (her ne kadar bir entegrasyon testi model bağlamayı kullansa da). Bu durumda model bağlama test edilmemiştir. Bu birim testleri yalnızca eylem yöntemindeki kodu test eder.

İkinci test, geçerli olduğunda ModelState bunu doğrular:

  • Depo aracılığıyla yeni bir BrainstormSession eklenmiştir.
  • yöntemi beklenen özelliklere sahip bir RedirectToActionResult döndürür.

Çağrılmayan sahte çağrılar normalde yoksayılır, ancak Verifiable kurulum çağrısının sonunda çağrılması testte mock doğrulamasına olanak tanır. Bu, mockRepo.Verify çağrısı ile gerçekleştirilir; beklenen yöntem çağrılmadıysa bu çağrı testi başarısız kılar.

Note

Bu örnekte kullanılan Moq kitaplığı, doğrulanabilir veya "katı" olan sahteleri doğrulanamayan sahtelerle ("gevşek" sahteler veya saplamalar olarak da adlandırılır) karıştırmayı mümkün kılar. Moq ile Sahte davranışı özelleştirme hakkında daha fazla bilgi edinin.

Örnek uygulamadaki SessionController , belirli bir beyin fırtınası oturumuyla ilgili bilgileri görüntüler. Denetleyici, geçersiz id değerlerini ele almak için mantık içerir (aşağıdaki örnekte bu durumları kapsamak için iki return senaryosu vardır). Son return ifade, görünüme yeni bir StormSessionViewModel döndürür (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);
    }
}

Birim testleri, Session denetleyicisindeki Index action'ında yer alan her return senaryo için bir test içerir:

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

Ideas denetleyicisine geçildiğinde uygulama, api/ideas rotasında bir web API'si olarak işlevselliği kullanıma sunar:

  • Bir beyin fırtınası oturumuyla ilişkili fikirlerin (IdeaDTO) listesi yöntemi tarafından ForSession döndürülür.
  • Create yöntemi bir oturuma yeni fikirler ekler.
[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);
}

İş etki alanı varlıklarını doğrudan API çağrıları aracılığıyla döndürmekten kaçının. Alan varlıkları:

  • Genellikle istemcinin gerektirdiğinden daha fazla veri içerir.
  • Uygulamanın iç etki alanı modelini gereksiz yere genel kullanıma sunulan API ile ilişkilendirin.

Etki alanı varlıkları ile istemciye dönen türler arasında eşleme gerçekleştirilebilir:

Ardından örnek uygulama, Ideas denetleyicisinin Create ve ForSession API yöntemleri için birim testlerini gösterir.

Örnek uygulama iki ForSession testi içerir. İlk test, ForSession öğesinin geçersiz bir oturum için NotFoundObjectResult (HTTP Not Found) döndürüp döndürmediğini belirler:

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

İkinci ForSession testi, ForSession öğesinin geçerli bir oturum için bir oturum fikirleri listesi (<List<IdeaDTO>>) döndürüp döndürmediğini belirler. Denetimler, özelliğinin doğru olduğunu onaylamak Name için ilk fikri de inceler:

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

ModelState geçersiz olduğunda Create yönteminin davranışını test etmek için örnek uygulama, testin bir parçası olarak denetleyiciye bir model hatası ekler. Birim testlerinde model doğrulamayı veya model bağlamayı test etmeye çalışmayın; yalnızca geçersiz bir ModelState ile karşılaştığınızda eylem yönteminin davranışını test edin:

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

İkinci test, deponun null döndürmesine bağlıdır, bu nedenle sahte depo null döndürecek şekilde yapılandırılmıştır; Create. Bir test veritabanı (bellekte veya başka bir şekilde) oluşturmanıza ve bu sonucu döndüren bir sorgu oluşturmanıza gerek yoktur. Örnek kodun gösterdiği gibi, bu test tek bir ifadeyle yapılabilir:

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

Üçüncü Create test olan Create_ReturnsNewlyCreatedIdeaForSession, deponun UpdateAsync yönteminin çağrıldığını doğrular. Mock, Verifiable ile çağrılır ve doğrulanabilir yöntemin yürütüldüğünü onaylamak için sahte deponun Verify yöntemi çağrılır. UpdateAsync yönteminin verileri kaydettiğinden emin olmak birim testinin sorumluluğu değildir. Bu işlem tümleştirme testiyle gerçekleştirilebilir.

[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 EylemSonucu<T>

ActionResult<T> (ActionResult<TValue>), ActionResult türünden türeyen bir tür veya belirli bir tür döndürebilir.

Örnek uygulama, belirli bir oturum id için bir List<IdeaDTO> döndüren bir yöntem içerir. Oturum id yoksa denetleyici döndürür 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;
}

denetleyicinin ForSessionActionResult iki testi içinde ApiIdeasControllerTestsyer alır.

İlk test, denetleyicinin ActionResult döndürdüğünü, ancak var olmayan bir ActionResult oturumu için var olmayan bir fikir listesi döndürmediğini onaylar:

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

Geçerli bir oturum idiçin, ikinci test yöntemin döndürdüğünü onaylar:

  • List<IdeaDTO> türüne sahip bir ActionResult.
  • ActionResult<T>.Value bir List<IdeaDTO> türdür.
  • Listedeki ilk öğe, GetTestSession çağrısı yapılarak elde edilen ve mock oturumunda depolanan fikirle eşleşen geçerli bir fikirdir.
[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);
}

Örnek uygulama ayrıca verilen bir oturum için yeni bir Idea oluşturma yöntemi de içerir. Denetleyici şunu döndürür:

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

ApiIdeasControllerTests içinde CreateActionResult için üç test yer alır.

İlk metin, geçersiz bir model için BadRequest döndürüldüğünü onaylar.

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

İkinci test, oturum yoksa NotFound döndürüldüğünü denetler.

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

Geçerli bir oturum idiçin son test şunları onaylar:

  • Yöntem, BrainstormSession türünde bir ActionResult döndürür.
  • ActionResult<T>.Result bir CreatedAtActionResult. CreatedAtActionResult, üst bilgisi içeren bir 201 Created yanıtına benzerdir.
  • ActionResult<T>. Değer bir BrainstormSession türdür.
  • Oturumu güncelleştirmek için yapılan sahte çağrı, UpdateAsync(testSession), çağrıldı. Verifiable yöntem çağrısı, doğrulamalarda mockRepo.Verify() çalıştırılarak denetlenir.
  • Oturum için iki Idea nesne döndürülür.
  • Son öğe (UpdateAsync için yapılan sahte çağrının eklediği Idea), testte oturuma eklenen newIdea öğesiyle eşleşir.
[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);
}

Denetleyiciler herhangi bir ASP.NET Core MVC uygulamasında merkezi bir rol oynar. Bu nedenle, denetleyicilerin amaçlandığı şekilde davrandığından emin olabilirsiniz. Otomatikleştirilmiş testler, uygulama bir üretim ortamına dağıtılmadan önce hataları algılayabilir.

Örnek kodu görüntüleme veya indirme (nasıl indirilir)

Denetleyici mantığının birim testleri

Birim testleri, bir uygulamanın bir bölümünü altyapısından ve bağımlılıklarından yalıtarak test etmeyi içerir. Denetleyici mantığına birim testi uygulandığında, bağımlılıklarının veya çerçevenin davranışı değil, yalnızca tek bir eylemin içeriği test edilir.

Denetleyicinin davranışına odaklanmak için denetleyici eylemlerinin birim testlerini ayarlayın. Denetleyici birim testi filtreler, yönlendirme ve model bağlama gibi senaryolardan kaçınır. Bir isteğe toplu olarak yanıt veren bileşenler arasındaki etkileşimleri kapsayan testler tümleştirme testleri ile ele alınır. Tümleştirme testleri hakkında daha fazla bilgi için bkz. ASP.NET Core'da tümleştirme testleri.

Özel filtreler ve yönlendirmeler yazıyorsanız, bunları belirli bir denetleyici eylemine yönelik testlerin parçası olarak değil, izole şekilde birim testiyle test edin.

Denetleyici birim testlerini göstermek için örnek uygulamada yer alan aşağıdaki denetleyiciyi gözden geçirin. Denetleyici Home , beyin fırtınası oturumlarının listesini görüntüler ve POST isteğiyle yeni beyin fırtınası oturumları oluşturulmasına olanak tanır:

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

Önceki denetleyici:

Yöntemin HTTP GET Index döngü veya dallanma yoktur ve yalnızca bir yöntemi çağırır. Bu eylem için birim testi:

  • IBrainstormSessionRepository hizmetini GetTestSessions yöntemini kullanarak taklit eder. GetTestSessions tarih ve oturum adlarıyla iki sahte beyin fırtınası oturumu oluşturur.
  • Index metodunu yürütür.
  • yöntem tarafından döndürülen sonuç üzerinde doğrulamalar yapar:
[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;
}

Denetleyicinin HomeHTTP POST Index yöntemi şunu doğrular:

  • ModelState.IsValid olduğundafalse, eylem yöntemi uygun verilerle 400 Hatalı İstek ViewResult.
  • ModelState.IsValid, true olduğunda:
    • Repository üzerindeki Add yöntemi çağrılır.
    • doğru RedirectToActionResult bağımsız değişkenlerle döndürülür.

Geçersiz bir model durumu, aşağıdaki ilk testte gösterildiği gibi kullanılarak AddModelError hata eklenerek test edilir:

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

ModelState geçerli olmadığında, get isteğiyle aynı ViewResult değer döndürülür. Test geçersiz bir model geçirmeyi denemez. Model bağlaması çalışmadığından (her ne kadar bir tümleştirme testi model bağlamayı kullansa da) geçersiz bir model geçirmek geçerli bir yaklaşım değildir. Bu durumda model bağlama test edilmez. Bu birim testleri yalnızca eylem yöntemindeki kodu test ederler.

İkinci test, ModelState geçerli olduğunda bunu doğrular:

  • Yeni BrainstormSession bir eklenmiştir (depo aracılığıyla).
  • Yöntem, beklenen özelliklere sahip bir RedirectToActionResult döndürür.

Çağrılmayan sahte çağrılar normalde yoksayılır, ancak Verifiable kurulum çağrısının sonunda çağrılması testte mock doğrulamasına olanak tanır. Bu, mockRepo.Verify çağrısı ile gerçekleştirilir; beklenen yöntem çağrılmadıysa testi başarısız kılar.

Note

Bu örnekte kullanılan Moq kitaplığı, doğrulanabilir veya "katı" olan mock nesnelerini doğrulanamayan mock nesneleriyle ("gevşek" mock nesneleri veya stub'lar olarak da adlandırılır) karıştırmayı mümkün kılar. Moq ile Mock davranışını özelleştirme hakkında daha fazla bilgi edinin.

Örnek uygulamadaki SessionController , belirli bir beyin fırtınası oturumuyla ilgili bilgileri görüntüler. Denetleyici, geçersiz id değerleri ele almak için mantık içerir (aşağıdaki örnekte bu senaryoları kapsamak için iki return senaryo vardır). Son return ifade, görünüme yeni bir StormSessionViewModel döndürür (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);
    }
}

Birim testleri, Oturum denetleyicisindeki her return senaryo için Index eyleminde bir test içerir:

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

Ideas denetleyicisine geçildiğinde uygulama, api/ideas yolunda bir web API'si olarak işlevsellik sunar:

  • Bir beyin fırtınası oturumuyla ilişkili fikirlerin (IdeaDTO) listesi ForSession yöntemi tarafından döndürülür.
  • Create yöntemi bir oturuma yeni fikirler ekler.
[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);
}

İş etki alanı varlıklarını doğrudan API çağrıları aracılığıyla döndürmekten kaçının. Etki alanı varlıkları:

  • Genellikle istemcinin gerektirdiğinden daha fazla veri içerirler.
  • Uygulamanın iç etki alanı modelini gereksiz yere dışa açık API'ye bağlayın.

Etki alanı varlıkları ile istemciye döndürülen türler arasında eşleme gerçekleştirilebilir:

Ardından örnek uygulama, Ideas denetleyicisinin Create ve ForSession API yöntemleri için birim testlerini gösterir.

Örnek uygulama iki ForSession test içerir. İlk test, ForSession geçersiz bir oturum için NotFoundObjectResult (HTTP Not Found) döndürüp döndürmediğini belirler:

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

İkinci ForSession test, geçerli bir oturum için oturum fikirlerinin () listesinin döndürülpForSession döndürülmediğini <List<IdeaDTO>> belirler. Denetimler, ilk fikri de Name özelliğinin doğru olduğunu doğrulamak için inceler:

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

geçersiz olduğunda Create yönteminin ModelState davranışını test etmek için örnek uygulama, test kapsamında denetleyiciye bir model hatası ekler. Birim testlerinde model doğrulamayı veya model bağlamayı test etmeye çalışmayın; yalnızca geçersiz bir ModelStatemodelle karşılaştığınızda eylem yönteminin davranışını test edin:

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

Create öğesinin ikinci testi, deponun null döndürmesine bağlıdır, bu nedenle sahte depo, null döndürecek şekilde yapılandırılır. Bir test veritabanı (bellekte veya başka bir şekilde) oluşturmanıza ve bu sonucu döndüren bir sorgu oluşturmanıza gerek yoktur. Test, örnek kodda gösterildiği gibi tek bir deyimde gerçekleştirilebilir:

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

Üçüncü Create test olan Create_ReturnsNewlyCreatedIdeaForSession, deponun UpdateAsync yönteminin çağrıldığını doğrular. Mock, Verifiable ile çağrılır ve doğrulanabilir yöntemin yürütüldüğünü onaylamak için sahte deponun Verify yöntemi çağrılır. UpdateAsync yönteminin verileri kaydettiğinden emin olmak birim testinin sorumluluğu değildir. Bu işlem tümleştirme testiyle gerçekleştirilebilir.

[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 EylemSonucu<T>

ASP.NET Core 2.1 veya sonraki sürümlerde, ActionResult<T> (ActionResult<TValue>), ActionResult türünden türetilen bir tür veya belirli bir tür döndürmenizi sağlar.

Örnek uygulama, belirli bir oturum idiçin bir List<IdeaDTO> döndüren bir yöntem içerir. Oturum id yoksa denetleyici döndürür 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;
}

ForSessionActionResult denetleyicisinin iki testi ApiIdeasControllerTests içinde yer alır.

İlk test, denetleyicinin bir ActionResult döndürdüğünü, ancak var olmayan bir id için var olmayan bir fikir listesi döndürmediğini doğrular:

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

Geçerli bir oturum için id, ikinci test yöntemin şunu döndürdüğünü doğrular:

  • List<IdeaDTO> türüne sahip bir ActionResult.
  • ActionResult<T>.Value bir List<IdeaDTO> türdür.
  • Listedeki ilk öğe, sahte oturumda depolanan fikirle eşleşen geçerli bir fikirdir (GetTestSession çağrısıyla elde edilen).
[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);
}

Örnek uygulama ayrıca belirli bir oturum için yeni bir Idea oluşturma yöntemi de içerir. Denetleyici şunu döndürür:

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

ApiIdeasControllerTests içinde CreateActionResult ile ilgili üç test yer alır.

İlk metin, geçersiz bir model için BadRequest döndürüldüğünü onaylar.

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

İkinci test, oturum mevcut değilse NotFound döndürüldüğünü denetler.

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

Geçerli bir oturum idiçin son test şunları onaylar:

  • yöntemi türüne sahip bir ActionResultBrainstormSession döndürür.
  • ActionResult<T>.Result bir CreatedAtActionResult. CreatedAtActionResult, üst bilgi içeren 201 Created.
  • ActionResult<T>.Value bir BrainstormSession türdür.
  • Oturumu güncelleyen sahte çağrı, UpdateAsync(testSession), çağrıldı. Verifiable yöntem çağrısı, onaylarda mockRepo.Verify() çalıştırılarak denetlenir.
  • Oturum için iki Idea nesne döndürülür.
  • Son öğe ( Idea sahte çağrı UpdateAsynctarafından öğesinin eklenmesi) testteki oturuma eklenen öğeyle eşleşir newIdea .
[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);
}