Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange...

45
Unit Testing in .NET

Transcript of Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange...

Page 1: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Unit Testing in .NET

Page 2: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Philosophy of Unit Testing

• What?

• Where?

• Why?

• How?

• What it is not?

Page 3: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

• Test individual components (units)

• in isolation

• to confirm preexisting specification.

• Inputs … -> expected results

• Test functions, methods.

• Use a framework to make it easier (xUnit or ?Unit)

isolate and validate

verify

Page 4: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

• Write tests after creating specification, but before implementing methods.

• Best if written by the developer or the development team. Why?

• Test-driven development

• Supports regression testing

Page 5: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

The Art of Unit Testing, by R Osherov

Page 6: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 7: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 8: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 9: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

1 Unit Test

assertions

Page 10: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Test Suite

Test Case

Test Case

Test Case

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Unit testUnit testUnit test

Run with TestRunner

Page 11: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Assertions

http://junit.sourceforge.net/javadoc/org/junit/Assert.html

JUnit

assertEquals() assertFalse() assertNotNull() assertNotSame() assertNull() assertSame() …

By the way, you should use assertions in everyday programming

Java: assert i > 0; C#: Debug.Assert( i > 0 ); Python: assert i > 0

Page 12: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

NUnit Assertions

Classic ModelAssert.isEmpty() Assert.areEqual() Assert.Null() Assert.notNull() …

https://github.com/nunit/docs/wiki/Classic-Model

Constraint Model

Assert.That()

https://github.com/nunit/docs/wiki/Constraint-Model

Page 13: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

NUnit

Assert.That( aString, Is.Empty ); Assert.That( aString, Is.Not.Empty ); Assert.That( 2 + 2, Is.EqualTo(4.0) ); Assert.That( 5.5, Is.EqualTo( 5 ).Within(1.5).Percent ); Assert.That( "Hello!", Is.Not.EqualTo( "HELLO!" ) ); Assert.That( "Hello!", Is.EqualTo( "HELLO!" ).IgnoreCase );

constraint

Page 14: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Good Habits• Try to make tests independent or orthogonal to other tests

• Test only one code unit at a time

• Don’t name your tests: test0, test1, …

• Use setup and teardown to create a clean environment for each test

• Don’t have unit tests access your database — use fake data (write a “mock” object that meets the same interface) ==> break dependencies

• Refactor to make your code more testable

• Discover a flaw at the component level? Write unit tests first to expose it (reproduce it). Then fix the error.

• Run tests prior to committing (or merging) your code

Page 15: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Start Simple• Test methods with no dependencies

• Make sure constructor doesn’t have dependencies

input Method output

Page 16: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

NUnit Demo (2017)• Add a new project to your solution: Visual C# -> Test

• Remove MSTest parts: reference, using and packages.config

Microsoft.VisualStudio.QualityTools.UnitTestFramework //using Microsoft.VisualStudio.TestTools.UnitTesting <package id=“MSTest.TestAdapter” version=“1.1.11” targetFramework=“net452” /> …

• Add NUnit to your test project using NuGet, packages:

NUnit v3.2.0

NUnit3TestAdapter v.3.0.9-ctp-9 (needs “Include prerelease” checked)

• Add MVC framework using NuGet (since separate project isn’t an MVC app)

Microsoft.AspNet.Mvc (Match the version # of your main project, mine was v.5.2.3)

• Add reference to your own project

Had to delete %AppData$/NuGet/NuGet.Config to load “online” package sources

Page 17: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

• Add Visual Studio Test Generator

• Optionally install Visual Studio Test Adapter as an extension rather than locally within the project

• Restart Visual Studio and finish installation

Page 18: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Find something to test

Page 19: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Write your unit test class:

Page 20: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Run Tests: Test -> Windows -> Test Explorer

Page 21: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Harder

input

Function

output

Database

Internet

Page 22: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Not Unit Testable

Page 23: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Not Unit Testable

Page 24: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Why Not?

• “Uncontrolled” dependencies

• All or most functionality in 1 method, with no separation and no “control” over it

• Code is doing more than 1 thing

Page 25: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Arrange — Act — Assert• Arrange

Set up for the test. Configure the system one way or another. Must be able to do this.

• Act

Execute or invoke something, with some input, to obtain some output. Must be able to do this.

• Assert

Usually the easy part.

Page 26: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

SOLID• Single responsibility principle

a class (module, method or function) should do one thing and one thing only

• Open/closed principle

• Liskov substitution principle it should be possible to substitute in fakes, stubs or mocks without fundamentally changing anything

• Interface segregation principle create segregation of responsibilities and become agnostic of implementors by replacing concrete classes with interfaces (having the minimum of functionality required)

• Dependency inversion principle (and Inversion of Control) invert the “high-level module depends on low-level module” pattern. Break direct dependencies by using dependency injection pattern and providers.

https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

Page 27: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

The Art of Unit Testing, by R Osherov

Page 28: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 29: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 30: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 31: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 32: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 33: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Problem?

Just a stub for now

Page 34: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

One Solution: inject via

constructor

will be other logic here, i.e. test the name not just the extension

Page 35: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 36: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do
Page 37: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

• Up Next …

• Testing code that reads/writes to a database

• Use a “mocking framework” to make it easier, i.e.

• Moq: https://github.com/Moq/moq4

• Use [SetUp] and [TearDown] to seed your mock object

Page 38: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Controller

DbContext

EntityFramework

Database

Default Setup

Page 39: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Controller

DbContext

EntityFramework

Database

Using Repository Pattern

ActualRepoForTestingRepo

ISomethingRepository

An interface

Page 40: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Need to modify to allow constructor

injection

Page 41: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Inject mock object

Page 42: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Sports Store Example from

Entity - Single table

Page 43: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Break dependency with Repository pattern

Page 44: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

pagination

constructor injection

Page 45: Unit Testing inmorses/classes/cs46x/lecture/UnitTesting.pdfArrange — Act — Assert • Arrange Set up for the test. Configure the system one way or another. Must be able to do

Unit Test Pagination