Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Oleh Steve Smith
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat logika pengontrol pengujian unit, hanya konten tindakan tunggal yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Pengontrol pengujian unit
Siapkan pengujian unit tindakan pengontrol untuk fokus pada perilaku pengontrol. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit dalam isolasi, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk menunjukkan pengujian unit pengontrol, tinjau pengontrol berikut di aplikasi sampel.
Melihat atau mengunduh kode sampel (cara mengunduh)
Pengontrol Home menampilkan daftar sesi curah otak dan memungkinkan pembuatan sesi curah otak baru dengan permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Pengontrol sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan injeksi dependensi (DI) untuk memberikan instans .
IBrainstormSessionRepository - Dapat diuji dengan layanan tiruan
IBrainstormSessionRepositorymenggunakan kerangka kerja objek tiruan, seperti Moq. Objek yang ditiru adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Pengujian unit untuk tindakan ini:
- Mengejek
IBrainstormSessionRepositorylayanan menggunakan metode .GetTestSessionsGetTestSessionsmembuat dua sesi curah gagasan tiruan dengan tanggal dan nama sesi. -
IndexMenjalankan metode . - Membuat pernyataan pada hasil yang dikembalikan oleh metode :
- ViewResult dikembalikan.
-
ViewDataDictionary.Model adalah
StormSessionViewModel. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode Home pengontrol HTTP POST Index menguji bahwa:
- Ketika ModelState.IsValid adalah
false, metode tindakan mengembalikan PermintaanViewResult Buruk 400 dengan data yang sesuai. - Kapan
ModelState.IsValidadalahtrue:- Metode
Addpada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[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();
}
Saat ModelState tidak valid, hal yang sama ViewResult dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk lulus dalam model yang tidak valid. Melewati model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode tindakan.
Pengujian kedua memverifikasi bahwa ketika ModelState valid:
-
BrainstormSessionBaru ditambahkan (melalui repositori). - Metode mengembalikan dengan properti yang
RedirectToActionResultdiharapkan.
Panggilan yang ditiru yang tidak dipanggil biasanya diabaikan, tetapi panggilan Verifiable di akhir panggilan penyiapan memungkinkan validasi tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify, yang gagal dalam pengujian jika metode yang diharapkan tidak dipanggil.
Note
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id (ada dua return skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan akhir return mengembalikan yang baru StormSessionViewModel ke tampilan (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);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return skenario dalam tindakan Pengontrol Index sesi:
[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);
}
Pindah ke pengontrol Ideas, aplikasi mengekspos fungsionalitas sebagai API web pada api/ideas rute:
- Daftar ide (
IdeaDTO) yang terkait dengan sesi curah gagasan dikembalikan olehForSessionmetode . - Metode ini
Createmenambahkan ide baru ke sesi.
[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);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Tidak perlu menggabungkan model domain internal aplikasi dengan API yang diekspos secara publik.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis dengan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk Create metode API dan ForSession pengontrol Ideas.
Aplikasi sampel berisi dua ForSession pengujian. Pengujian pertama menentukan apakah ForSession mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[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);
}
Pengujian kedua ForSession menentukan apakah ForSession mengembalikan daftar ide sesi (<List<IdeaDTO>>) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name benar:
[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);
}
Untuk menguji perilaku Create metode ketika ModelState tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create kedua tergantung pada repositori yang mengembalikan null, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[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);
}
Pengujian ketiga Create , Create_ReturnsNewlyCreatedIdeaForSession, memverifikasi bahwa metode repositori UpdateAsync dipanggil. Tiruan dipanggil dengan Verifiable, dan metode repositori Verify yang ditiru dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa UpdateAsync metode menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[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);
}
Uji ActionResult<T>
ActionResult<T> (ActionResult<TValue>) dapat mengembalikan jenis yang berasal dari ActionResult atau mengembalikan jenis tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO> untuk sesi idtertentu . Jika sesi id tidak ada, pengontrol mengembalikan 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;
}
Dua pengujian ForSessionActionResult pengontrol disertakan dalam ApiIdeasControllerTests.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult tetapi bukan daftar ide yang tidak ada untuk sesi idyang tidak ada :
- Jenisnya
ActionResultadalahActionResult<List<IdeaDTO>>. - Result adalah .NotFoundObjectResult
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Dengan
ActionResultList<IdeaDTO>jenis. - ActionResult <T>. Nilai adalah
List<IdeaDTO>jenis. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat baru Idea untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ada.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[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);
}
Tiga pengujian CreateActionResult disertakan dalam ApiIdeasControllerTests.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[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);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[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);
}
Untuk sesi idyang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResultdenganBrainstormSessionjenis. - ActionResult <T>. Hasilnya adalah CreatedAtActionResult.
CreatedAtActionResultdianalogikan dengan respons 201 Dibuat denganLocationheader. - ActionResult <T>. Nilai adalah
BrainstormSessionjenis. - Panggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession), dipanggil. PanggilanVerifiablemetode diperiksa dengan menjalankanmockRepo.Verify()dalam pernyataan. - Dua
Ideaobjek dikembalikan untuk sesi tersebut. - Item terakhir (yang
Ideaditambahkan oleh panggilan tiruan keUpdateAsync) cocok dengan yangnewIdeaditambahkan ke sesi dalam pengujian.
[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);
}
Pengontrol memainkan peran terpusat dalam aplikasi MVC Core ASP.NET apa pun. Dengan demikian, Anda harus memiliki keyakinan bahwa pengontrol bereaksi seperti yang diinginkan. Pengujian otomatis dapat mendeteksi kesalahan sebelum aplikasi disebarkan ke lingkungan produksi.
Melihat atau mengunduh kode sampel (cara mengunduh)
Pengujian unit logika pengontrol
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat logika pengontrol pengujian unit, hanya konten tindakan tunggal yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Siapkan pengujian unit tindakan pengontrol untuk fokus pada perilaku pengontrol. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit dalam isolasi, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk menunjukkan pengujian unit pengontrol, tinjau pengontrol berikut di aplikasi sampel. Pengontrol Home menampilkan daftar sesi curah otak dan memungkinkan pembuatan sesi curah otak baru dengan permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Pengontrol sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan injeksi dependensi (DI) untuk memberikan instans .
IBrainstormSessionRepository - Dapat diuji dengan layanan tiruan
IBrainstormSessionRepositorymenggunakan kerangka kerja objek tiruan, seperti Moq. Objek yang ditiru adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Pengujian unit untuk tindakan ini:
- Mengejek
IBrainstormSessionRepositorylayanan menggunakan metode .GetTestSessionsGetTestSessionsmembuat dua sesi curah gagasan tiruan dengan tanggal dan nama sesi. -
IndexMenjalankan metode . - Membuat pernyataan pada hasil yang dikembalikan oleh metode :
- ViewResult dikembalikan.
-
ViewDataDictionary.Model adalah
StormSessionViewModel. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode Home pengontrol HTTP POST Index menguji bahwa:
- Ketika ModelState.IsValid adalah
false, metode tindakan mengembalikan PermintaanViewResult Buruk 400 dengan data yang sesuai. - Kapan
ModelState.IsValidadalahtrue:- Metode
Addpada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[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();
}
Saat ModelState tidak valid, hal yang sama ViewResult dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk lulus dalam model yang tidak valid. Melewati model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode tindakan.
Pengujian kedua memverifikasi bahwa ketika ModelState valid:
-
BrainstormSessionBaru ditambahkan (melalui repositori). - Metode mengembalikan dengan properti yang
RedirectToActionResultdiharapkan.
Panggilan yang ditiru yang tidak dipanggil biasanya diabaikan, tetapi panggilan Verifiable di akhir panggilan penyiapan memungkinkan validasi tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify, yang gagal dalam pengujian jika metode yang diharapkan tidak dipanggil.
Note
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id (ada dua return skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan akhir return mengembalikan yang baru StormSessionViewModel ke tampilan (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);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return skenario dalam tindakan Pengontrol Index sesi:
[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);
}
Pindah ke pengontrol Ideas, aplikasi mengekspos fungsionalitas sebagai API web pada api/ideas rute:
- Daftar ide (
IdeaDTO) yang terkait dengan sesi curah gagasan dikembalikan olehForSessionmetode . - Metode ini
Createmenambahkan ide baru ke sesi.
[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);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Tidak perlu menggabungkan model domain internal aplikasi dengan API yang diekspos secara publik.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis dengan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk Create metode API dan ForSession pengontrol Ideas.
Aplikasi sampel berisi dua ForSession pengujian. Pengujian pertama menentukan apakah ForSession mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[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);
}
Pengujian kedua ForSession menentukan apakah ForSession mengembalikan daftar ide sesi (<List<IdeaDTO>>) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name benar:
[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);
}
Untuk menguji perilaku Create metode ketika ModelState tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create kedua tergantung pada repositori yang mengembalikan null, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[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);
}
Pengujian ketiga Create , Create_ReturnsNewlyCreatedIdeaForSession, memverifikasi bahwa metode repositori UpdateAsync dipanggil. Tiruan dipanggil dengan Verifiable, dan metode repositori Verify yang ditiru dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa UpdateAsync metode menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[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);
}
Uji ActionResult<T>
Di ASP.NET Core 2.1 atau yang lebih baru, ActionResult<T> (ActionResult<TValue>) memungkinkan Anda mengembalikan jenis yang berasal dari ActionResult atau mengembalikan jenis tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO> untuk sesi idtertentu . Jika sesi id tidak ada, pengontrol mengembalikan 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;
}
Dua pengujian ForSessionActionResult pengontrol disertakan dalam ApiIdeasControllerTests.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult tetapi bukan daftar ide yang tidak ada untuk sesi idyang tidak ada :
- Jenisnya
ActionResultadalahActionResult<List<IdeaDTO>>. - Result adalah .NotFoundObjectResult
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi idyang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Dengan
ActionResultList<IdeaDTO>jenis. - ActionResult <T>. Nilai adalah
List<IdeaDTO>jenis. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat baru Idea untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ada.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[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);
}
Tiga pengujian CreateActionResult disertakan dalam ApiIdeasControllerTests.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[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);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[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);
}
Untuk sesi idyang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResultdenganBrainstormSessionjenis. - ActionResult <T>. Hasilnya adalah CreatedAtActionResult.
CreatedAtActionResultdianalogikan dengan respons 201 Dibuat denganLocationheader. - ActionResult <T>. Nilai adalah
BrainstormSessionjenis. - Panggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession), dipanggil. PanggilanVerifiablemetode diperiksa dengan menjalankanmockRepo.Verify()dalam pernyataan. - Dua
Ideaobjek dikembalikan untuk sesi tersebut. - Item terakhir (yang
Ideaditambahkan oleh panggilan tiruan keUpdateAsync) cocok dengan yangnewIdeaditambahkan ke sesi dalam pengujian.
[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);
}
Sumber daya tambahan
- Pengujian integrasi di ASP.NET Core
- Membuat dan menjalankan pengujian unit dengan Visual Studio
- MyTested.AspNetCore.Mvc - Pustaka Pengujian Fasih untuk ASP.NET Core MVC: Pustaka pengujian unit dengan jenis kuat, menyediakan antarmuka yang fasih untuk menguji aplikasi MVC dan API web. (Tidak dipertahankan atau didukung oleh Microsoft.)
- JustMockLite: Kerangka kerja tiruan untuk pengembang .NET. (Tidak dipertahankan atau didukung oleh Microsoft.)
ASP.NET Core