0% found this document useful (0 votes)
27 views38 pages

6.fall 23-Lecture7UnitTesting

This document discusses unit testing and test automation. It covers writing and executing unit tests using JUnit, including test fixtures for shared initialization and teardown. Key aspects covered include writing test cases for individual classes or methods, using assertions to compare expected and actual outputs, and conventions like naming test classes after the class being tested.

Uploaded by

saminayeem2
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views38 pages

6.fall 23-Lecture7UnitTesting

This document discusses unit testing and test automation. It covers writing and executing unit tests using JUnit, including test fixtures for shared initialization and teardown. Key aspects covered include writing test cases for individual classes or methods, using assertions to compare expected and actual outputs, and conventions like naming test classes after the class being tested.

Uploaded by

saminayeem2
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 38

Unit Testing and Test

Automation
CSE 4495 - Lecture 7 - 16/08/2022
Instructor : Md. Mohaiminul Islam

1
Today’s Goals

• Unit Testing
• Testing of individual classes
• Writing and executing test cases
• How to write unit tests in JUnit.
• Executing tests as part of a build script.

2018-08-27 Chalmers University of Technology 2


Testing Stages
API GUI CLI

• We interact with systems


API
through interfaces.
• APIs, GUIs, CLIs
• Systems built from subsystems.
• With their own interfaces. API

• Subsystems built from units.


• Communication via method calls.
• Set of methods is an interface.
Unit Testing

• Testing the smallest “unit” that can be tested.


• Often, a class and its methods.
• Tested in isolation from all other units.
• Mock the results from other classes.
• Test input = method calls.
• Test oracle = assertions on output/class variables.
Unit Testing
Account

• For a unit, tests should: - name


- personnummer
- balance
• Test all “jobs” associated with the unit.
• Individual methods belonging to a class. Account (name,
• Sequences of methods that can interact. personnummer, Balance)

• Set and check class variables. withdraw (double amount)


deposit (double amount)
• Examine how variables change after changeName(String name)
getName()
method calls. getPersonnummer()
getBalance()
• Put the variables into all possible states
(types of values).
Unit Testing - Account

Account Unit tests should cover:


- name ● Set and check class variables.
- personnummer
- balance ○ Can any methods change name,
personnummer, balance?
Account (name, ○ Does changing those create
personnummer, Balance)
problems?
withdraw (double amount)
deposit (double amount)
changeName(String name)
● Each “job” performed by the
getName()
getPersonnummer() class.
getBalance()
○ Single methods or method sequences.
■ Vary the order methods are called.
○ Each outcome of each “job” (error
Unit Testing - Account

Account
Some tests we might want to write:
- name • Execute constructor, verify fields.
- personnummer
- balance • Check the name, change the name, make
sure changed name is in place.
Account (name,
personnummer, Balance) • Check that personnummer is correct.
withdraw (double amount)
deposit (double amount) • Check the balance, withdraw money,
changeName(String name)
getName() verify that new balance is correct.
getPersonnummer()
getBalance() • Check the balance, deposit money,
verify that new balance is correct.
Unit Testing - Account

Account
Some potential error cases:
--name • Withdraw more than is in balance.
name
- personnummer
--balance
personnummer • Withdraw a negative amount.
- balance
• Deposit a negative amount.
Account
withdraw (name,
(double amount)
personnummer, Balance) • Withdraw/Deposit a small amount
deposit (double amount)
changeName(String
withdraw name)
(double amount) (potential rounding error)
deposit (double amount)
getName()
changeName(String
getPersonnummer() name) • Change name to a null reference.
getName()
getBalance()
getPersonnummer()
getBalance() • Can we set an “malformed” name?
• (i.e., are there any rules on a valid
name?)
Unit Testing and Test Automation
Writing a Unit Test

JUnit is a Java-based toolkit public class Calculator {


for writing executable tests. public int evaluate(String expression){
• Choose a target from the int sum = 0;
for (String summand
code base.
:expression.split("\\+"))
• Write a “testing class”
sum += Integer.valueOf(summand);
containing a series of unit return sum;
tests centered around }
testing that target. }
JUnit Test Skeleton

@Test annotation defines a single test:


Type of scenario, and expectation on outcome.
@Test I.e., testEvaluate_GoodInput() or testEvaluate_NullInput()
public void test<Feature or Method Name>_<Testing Context>() {
//Define Inputs
try{ //Try to get output.
}catch(Exception error){
fail("Why did it fail?");
}
//Compare expected and actual values through assertions or through
//if-statements/fail commands
}
Writing JUnit Tests Convention - name the test class
after the class it is testing.
Each test is denoted with keyword import static org.junit.Assert.assertEquals;
@test.
import org.junit.Test;

public class Calculator {


public class CalculatorTest {
public int evaluate (String
@Test
expression) {
void
int sum = 0; testEvaluate_Valid_ShouldPa
Initialization ss(){
Calculator calculator = new Calculator();
for (String summand: int sum = calculator.evaluate("1+2+3");
Test Steps Input
expression.split("\\+")) assertEquals(6, sum);
Oracle
sum += Integer.valueOf(summand); }

return sum; }

}
}
Test Fixtures - Shared Initialization

@BeforeEach annotation defines a common test


initialization method:
@BeforeEach
public void setUp() throws Exception
{
this.registration = new Registration();
this.registration.setUser(“MoI”);
}
Test Fixtures - Teardown Method

@AfterEach annotation defines a common test tear


down method:
@AfterEach
public void tearDown() throws Exception
{
this.registration.logout();
this.registration = null;
}
More Test Fixtures
@BeforeAll
• @BeforeAll defines public static void setUpClass() {

initialization to take myManagedResource = new


ManagedResource();
place before any tests }
are run.
• @AfterAll defines @AfterAll
public static void tearDownClass()
tear down after all throws IOException {
tests are done. myManagedResource.close(
); myManagedResource =
null;
}
Assertions

Assertions are a "language" of testing - constraints that you


place on the output.

• assertEquals, assertArrayEquals
• assertFalse, assertTrue
• assertNull, assertNotNull
• assertSame,assertNotSame
assertEquals
@Test
public void testAssertEquals()
● Compares two items for
{ assertEquals("failure - strings are equality.
not ● For user-defined classes,
equal", "text", "text");
} relies on .equals method.
○ Compare field-by-field
@Test
○ assertEquals(studentA.getName(),
studentB.getName())
public void testAssertArrayEquals() rather than
{ byte[] expected = "trial".getBytes(); assertEquals(studentA, studentB)
byte[] actual = "trial".getBytes(); ● assertArrayEquals
assertArrayEquals("failure - byte arrays
not same", expected, actual); compares arrays of items.
}
assertFalse, assertTrue
@Test
public void testAssertFalse()
{ assertFalse("failure - should be
● Take in a string and a
false", boolean expression.
(getGrade(studentA, “CSE4495”).equals(“A+”)); ● Evaluates the expression
}
and issues pass/fail based on
@Test outcome.
public void testAssertTrue()
● Used to check conformance
{ assertTrue("failure - should be
true", of solution to expected
(getCGPA(studentA) > 3.5)); properties.
}
assertSame, assertNotSame
@Test
public void testAssertNotSame()
{ assertNotSame("should not be same ● Checks whether two
Object",
studentA, new Object()); objects are clones.
} ● Are these variables aliases
@Test
for the same object?
public void testAssertSame()
○ assertEquals uses
{ Student studentB = .equals().
studentA;
○ assertSame uses ==
assertSame("should be same", studentA,
studentB);
}
assertNull, assertNotNull
@Test
public void testAssertNotNull()
{ assertNotNull("should not be
● Take in an object and
null", new Object());
} checks whether it is
null/not null.
@Test ● Can be used to help
public void testAssertNull()
diagnose and void null
{ assertNull("should be null",
null);
pointer exceptions.
}
Grouping Assertions

@Test
void groupedAssertions() {
● Grouped assertions are
Person person = Account.getHolder(); executed.
assertAll("person", ○ Failures are reported
() -> assertEquals("John", together.
person.getFirstName()), ○ Preferred way to
() -> assertEquals("Doe",
compare fields of two
person.getLastName()));
data structures.
}
assertThat
@Test both - two properties must be met.
public void testAssertThat{
assertThat("albumen", both(containsString("a")).and(containsString("b")));
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }),
everyItem(containsString("n")));
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"),
equalTo("good")))); assertThat("good", anyOf(equalTo("bad"),
equalTo("good"))); assertThat(7,
not(CombinableMatcher.<Integer>
either(equalTo(3)).or(equalTo(4))));
}
assertThat
@Test everyItem - all items in list must match a
property.
public void testAssertThat{
assertThat("albumen", both(containsString("a")).and(containsString("b")));
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }),
everyItem(containsString("n")));
assertThat("godfather", allOf(equalTo("godfather"),
startsWith("go")));
assertThat("good", not(allOf(equalTo("bad"),
equalTo("good")))); assertThat("good", anyOf(equalTo("bad"),
equalTo("good"))); assertThat(7,
not(CombinableMatcher.<Integer>
either(equalTo(3)).or(equalTo(4))));
assertThat
@Test allOf - all listed properties must be true
public void testAssertThat{
assertThat("albumen", both(containsString("a")).and(containsString("b")));
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }),
everyItem(containsString("n")));
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"),
equalTo("good")))); assertThat("good", anyOf(equalTo("bad"),
equalTo("good"))); assertThat(7,
not(CombinableMatcher.<Integer>
either(equalTo(3)).or(equalTo(4))));
}
assertThat
@Test anyOf - at least one of the listed
properties must be true
public void testAssertThat{
assertThat("albumen", both(containsString("a")).and(containsString("b")));
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }),
everyItem(containsString("n")));
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"),
equalTo("good")))); assertThat("good", anyOf(equalTo("bad"),
equalTo("good"))); assertThat(7,
not(CombinableMatcher.<Integer>
either(equalTo(3)).or(equalTo(4))));
}
assertThat
@Test either - pass if one of these properties
public void testAssertThat{
assertThat("albumen", both(containsString("a")).and(containsString("b")));
assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
assertThat(Arrays.asList(new String[] { "fun", "ban", "net" }),
everyItem(containsString("n")));
assertThat("good", allOf(equalTo("good"), startsWith("good")));
assertThat("good", not(allOf(equalTo("bad"),
equalTo("good")))); assertThat("good", anyOf(equalTo("bad"),
equalTo("good"))); assertThat(7,
not(CombinableMatcher.<Integer>
either(equalTo(3)).or(equalTo(4))));
}
Testing Exceptions
@Test ● When testing error
void exceptionTesting() {
Throwable exception =
handling, we expect
assertThrows( IndexOutOfBoundsExc exceptions to be thrown.
eption.class, ○ assertThrows checks
() -> { new ArrayList<Object>().get(0);} whether the code block
); throws the expected
assertEquals("Index:0, Size:0", exception.
exception.getMessage());
○ assertEquals can be
} used to check the
contents of the stack
trace.
Testing Performance
@Test
void timeoutExceeded()
{ assertTimeout( ofMillis(1
● assertTimeout can be
0), () ->
{ Order.process(); });
used to impose a time
} limit on an action.
@Test ○ Time limit stated using ofMilis(..),
void timeoutNotExceededWithMethod() { ofSeconds(..), ofMinutes(..)
○ Result of action can be captured as well,
String greeting = allowing checking of result correctness.
assertTimeout(ofMinutes(2),
AssertionsDemo::greeting);
assertEquals("Hello, World!",
greeting);
Unit Testing - Account

Account
• Withdraw money, verify balance.
@Test
- name
- personnummer public void testWithdraw_normal() {
- balance // Setup
Account account = new Account(“Test MrTest”, “19850101-1001”,
Account (name, 48.5);
personnummer, Balance) // Test Steps
withdraw (double amount) double toWithdraw = 16.0; //Input
deposit (double amount) account.withdraw(toWithdraw);
changeName(String name)
getName() double actual = account.getBalance();
getPersonnummer()
getBalance() double expectedBalance = 32.5; // Oracle
assertEquals(expected, actual); // Oracle
}
Unit Testing - Account

Account • Withdraw more than is in balance.


- name • (should throw an exception with appropriate
- personnummer
- balance error message)
@Test
public void testWithdraw_moreThanBalance() {
Account (name, // Setup
personnummer, Balance)
Account account = new Account(“Test MrTest”, “19850101-
withdraw (double amount) 1001”, 48.5);
deposit (double amount)
changeName(String name) // Test Steps
getName() double toWithdraw = 100.0; //Input
getPersonnummer()
getBalance() Throwable exception = assertThrows(
() -> { account.withdraw(toWithdraw); } );
assertEquals(“Amount 100.00 is greater than balance
48.50”,exception.getMessage()); // Oracle
Unit Testing - Account

Account
• Withdraw a negative amount.
- name • (should throw an exception with
- personnummer
- balance appropriate error message)
@Test
public void testWithdraw_negative() {
Account (name,
personnummer, Balance) // Setup
Account account = new Account(“Test MrTest”, “19850101-1001”, 48.5);
withdraw (double amount) // Test Steps
deposit (double amount)
changeName(String name) double toWithdraw = -2.5; //Input
getName() Throwable exception = assertThrows(
getPersonnummer()
getBalance() () -> { account.withdraw(toWithdraw); } );
assertEquals(“Cannot withdraw a negative amount: -2.50”,
exception.getMessage()); // Oracle
}
Let’s take a break.
Best Practices

• Use assertions instead of print statements


@Test
public void testStringUtil_Bad() {
String result = stringUtil.concat("Hello ", "World");
System.out.println("Result is "+result);
}

@Test
public void testStringUtil_Good() {
String result = stringUtil.concat("Hello ", "World");
assertEquals("Hello World", result);
}

• The first will always pass (no


assertions)
Best Practices

• If code is non-deterministic, tests should give deterministic results.


public long calculateTime(){
long time = 0;
long before =
System.currentTimeMillis(
);
veryComplexFunction();
long after = System.currentTimeMillis();
time = after - before;
return time;
}
• Tests for this method should not specify exact time, but properties of a
“good” execution.
• The time should be positive, not negative or 0.
• A range on the allowed times.
Best Practices

• Test negative scenarios and boundary cases, in


addition to positive scenarios.
• Can the system handle invalid data?
• Method expects a string of length 8, with A-Z,a-z,0-9.
• Try non-alphanumeric characters. Try a blank value. Try strings with
length < 8, > 8
• Boundary cases test extreme values.
• If method expects numeric value 1 to 100, try 1 and 100.
• Also, 0, negative, 100+ (negative scenarios).
Best Practices

• Test only one unit at a time.


• Each scenario in a separate test case.
• Helps in isolating and fixing faults.
• Don’t use unnecessary assertions.
• Specify how code should work, not a list of observations.
• Generally, each unit test performs one assertion
• Or all assertions are related.
Best Practices

• Make each test independent of all others.


• Use @BeforeEach and @AfterEach to set up state and clear state before
the next test case.
• Create unit tests to target exceptions.
• If an exception should be thrown based on certain input, make sure
the exception is thrown.
Thank You

38

You might also like