66
Testing A brief introduction

Introduction to Software Testing

Embed Size (px)

DESCRIPTION

Brief introduction to testing talk prepared to creates a starting point at TAPTAP Networks & SONATA Usa Corp. The companies where I work as Q&A Team member.

Citation preview

Page 1: Introduction to Software Testing

TestingA brief introduction

Page 2: Introduction to Software Testing

Main concepts

Page 3: Introduction to Software Testing

“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence …”

Kent Beck

Page 4: Introduction to Software Testing

No silver bullets

Page 5: Introduction to Software Testing

What is testing about?

Page 6: Introduction to Software Testing

Getting feedback,as much frequent as we can

Page 7: Introduction to Software Testing

the sooner, the better

Page 8: Introduction to Software Testing

TAXONOMY

Page 9: Introduction to Software Testing

Taxo

nom

yScope

Unit

Integration

System

VisibillityBlack box

White box

Intention

Acceptance

Functional

Non Functional

TechniqueStatic

Dynamic

ExecutionAutomatic

Manual

Page 10: Introduction to Software Testing

Visibillity

Black box

White box

System

Integration

Unit

Intention

Acceptance

Functional

Non Functional

Technique

Dynamic

Static

Page 11: Introduction to Software Testing
Page 12: Introduction to Software Testing

Scope

Database

PORT1 PORT2

ADAPTER1

Page 13: Introduction to Software Testing

Unit test

Database

Page 14: Introduction to Software Testing

Dependency injection

•Dependency:A depends on B when A needs B to do its job.• Injection:

Object which uses A tells A who is B.

Page 15: Introduction to Software Testing

Dependency injection

• Separate business logic from creation logic• Avoid use of new for service objects.• Value objects can be created any where.

• Service objects in charge to implement business logic.• IOC Container or factories in charge of creation

logic.

Page 16: Introduction to Software Testing

Dependency injection

public UserService(UserValidator userValidator, UserDao userDao) {this.userValidator = userValidator;this.userDao = userDao;

}

public User createUser(User user) throws ValidationException {

this.userValidator.validate(user);

user = this.userDao.create(user); return user;}

public User createUser(User user) throws ValidationException {

UserValidator userValidator = new UserValidator(...);

userValidator.validate(user);

UserDao userDao = new UserDao(...);

user = userDao.create(user);return user;

}

VS

Page 17: Introduction to Software Testing

Dependency injection

public UserService(UserValidator userValidator, UserDao userDao) {this.userValidator = userValidator;this.userDao = userDao;

}

public User createUser(User user) throws ValidationException {

this.userValidator.validate(user);

user = this.userDao.create(user); return user;}

public User createUser(User user) throws ValidationException {

UserValidator userValidator = new UserValidator(...);

userValidator.validate(user);

UserDao userDao = new UserDao(...);

user = userDao.create(user);return user;

}

VSthis sucks

Page 18: Introduction to Software Testing

Test doubles

•Fake•Stub•Spy•Mock

Page 19: Introduction to Software Testing

Test doubles (Fake)

public UserDaoFake implements UserDao { @Override public User create(User user) { return new User(...); }}

Fake implementation in order to make test pass.

Page 20: Introduction to Software Testing

Test doubles (Stub)

UserValidator validatorMock = mock(UserValidator.class);

stub(validatorMock.validate(any(User.class))).toThrow(new ValidationException());

var validateCall = Sinon.stub();validatorStub.withArgs(user) .onFirstCall().returns(validationError);

var userValidator = { validate: validatorStub;}

OR WITH JS

Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.

Page 21: Introduction to Software Testing

Test doubles (Spy)Spies are objects that also record some information based on how they were called

var validatorSpy = Sinon.spy();var userValidator = { validate: validatorSpy;}userValidator.validate(user);sinon.assert.calledOnce(validatorSpy);sinon.assert.calledWith(validatorSpy, user);

OR WITH JS

UserValidator validatorSpy = spy(new UserValidator());doThrow(new ValidationException()).when(validatorSpy).validate();verify(validatorMock).validate(any(User.class))

Page 22: Introduction to Software Testing

Test doubles (Spy)Spies are objects that also record some information based on how they were called

var validatorSpy = Sinon.spy();var userValidator = { validate: validatorSpy;}userValidator.validate(user);sinon.assert.calledOnce(validatorSpy);sinon.assert.calledWith(validatorSpy, user);

OR WITH JS

UserValidator validatorSpy = spy(new UserValidator());doThrow(new ValidationException()).when(validatorSpy).validate();

Page 23: Introduction to Software Testing

Test doubles (Mocks)

UserValidator validatorMock = mock(UserValidator.class);when(validatorMock.validate(any(User.class))).thenTrhow(new ValidationException());

verify(validatorMock).validate(any(User.class))

Informal: think in a Stub which is also a Spy. It also responds with default values to non-explicitly declared methods

var validatorAPI = {validate: function()};var validatorMock = Sinon.mock(validatorAPI);validatorMock.expects('validate').once() .withArgs(user).throws(validationError)validatorAPI.validate(user)validatorMock.verify()

OR WITH JS

Page 24: Introduction to Software Testing

Integration test which want to be unit tests

Database

Page 25: Introduction to Software Testing

FIRST(IT)• Fast

Hundreds or thousands per second• Isolates

Failure reasons become obvious• Repeatable

In any order, any time• Self-validating

No manual execution required• Timely

Written before code• Immutable*

SUT is in the same state after execute the tests• Trusted*

When the test fails, the system fail and when the test works, the system works

Page 26: Introduction to Software Testing

Integration test which works with external system

Database

Page 27: Introduction to Software Testing

Integration test which uses the UI

Database

Page 28: Introduction to Software Testing

System test

Database

Page 29: Introduction to Software Testing

Who, when and where run the tests?

• Unit• Owner: developer• When: after every change• Where: every computer

• Integration• Owner: developer || QA team• When: as part or after commit stage• Where: devel and pre-pro environments

• System• Owner: QA team• When: as part or after commit stage• Where: devel and pre-pro environments

Page 30: Introduction to Software Testing

STRATEGIES

Page 31: Introduction to Software Testing

Static evaluation

• Informal review• Formal review (inspection)•Checklists• Sucessive abstraction•Walkthrough

Page 32: Introduction to Software Testing

Dynamic evaluation

• White box• Path Coverage• Statement Coverage• Condition Coverage• Function Coverage

• Black box• Equivalence partitioning• Boundary values analysis

Page 33: Introduction to Software Testing

White box (*-coverage)1. Get flow diagram of the SUT

2. Calculate cyclomatic complexity

3. Determine a data set which force going one path or another4. Exercise the SUT with this dataset.

...errors = []

if (user.name ==null || user.email == null) {

errors.push('mandatory fields not found');}//do the rest of whatever

for (var i=0; i < user.friends ; i++) { errors.push(checkFriendShipt(user.friends[i]))}...

Page 34: Introduction to Software Testing

White box (*-coverage)1. Get flow diagram of the SUT

2. Calculate cyclomatic complexity

3. Determine a data set which force going one path or another4. Exercise the SUT with this dataset.

a

b c

d

…x

...errors = []

if (user.name ==null || user.email == null) {

errors.push('mandatory fields not found');}//do the rest of whatever

for (var i=0; i < user.friends ; i++) { errors.push(checkFriendShipt(user.friends[i]))}...

Page 35: Introduction to Software Testing

White box (*-coverage)1. Get flow diagram of the SUT

2. Calculate cyclomatic complexity

3. Determine a data set which force going one path or another4. Exercise the SUT with this dataset.

edges – nodes + 2 = predicate nodes +1 = number of regions

a

b c

d

…x

...errors = []

if (user.name ==null || user.email == null) {

errors.push('mandatory fields not found');}//do the rest of whatever

for (var i=0; i < user.friends ; i++) { errors.push(checkFriendShipt(user.friends[i]))}...

Page 36: Introduction to Software Testing

Black box (partitioning)

1. Identify equivalence classes2. Select dataset:

1. Assign a unique value for every class2. Select tests cases which cover the most

valid classes3. Select tests cases which cover only one

invalid class at the same time

Page 37: Introduction to Software Testing

Black box (partitioning)

Register

Username*

Password (6-10 chars including numbers)

Page 38: Introduction to Software Testing

Black box (partitioning)

Register

Username*

Password (6-10 chars including numbers)

Username Password

U1: myNick P1: p4ssw0rd

U2: “empty” P2: p4ss

P3: l4rg3p4ssw0rd

P4: password

Page 39: Introduction to Software Testing

Black box (partitioning)

Register

Username*

Password (6-10 chars including numbers)

Username Password

U1: myNick P1: p4ssw0rd

U2: “empty” P2: p4ss

P3: l4rg3p4ssw0rd

P4: password

Test Cases myNick, p4ssw0rd √ myNick, p4ss X myNick, l4rg3p4ssw0rd X myNick, password X “empty”, p4ssw0rd X

Page 40: Introduction to Software Testing

AUTOMATIC TESTING

Page 41: Introduction to Software Testing

4 phases-tests

1. Set Up2. Exercise3. Verify4. TearDown

Page 42: Introduction to Software Testing

Testing frameworks families• X-Unit• @Before(Class)• @Test• @After(Class)

• Rspec• describe

• beforeEach• it• afterEach

• Specification by example (A.K.A BDD)• Given• When• Then

Page 43: Introduction to Software Testing

XUnit@Beforepublic void setUp() { this.userValidator = mock(UserValidator.class); this.userDao = mock(UserDao.class); this.userService = new UserService(userValidator, userDao);}

@Testpublic void createValidUserShouldNotFail() { //Exercise User expectedCreatedUser = new User("irrelevantUser"); when(userValidator.validate(any(User.class))); when(userValidator.validate(any(User.class))).thenReturn(createdUser); User createdUser = userService.create(new User()); //Assertions assertThat(createdUser, equalTo(expectedCreatedUser));}

@Test(expected=ValidationException)public void createInvalidUserShouldFail() { when(userValidator.validate(any(User.class))) .thenReturn(new ValidationException()); userService.create(new User("irrelevantUser"));}

@Afterpublic void tearDown() { //clean the state here}

Page 44: Introduction to Software Testing

Rspec (suite per class)describe('UserService test suite:', function(){ beforeEach(function(){ // setup the SUT })

it('when create a valid user should not fail', function(){ // exercise + assertions })

it('when create an invalid user should fail', function(){ // exercise + assertions })

afterEach(function(){ // clean the state })})

• UserService test suite:• When create a valid user should not fail √• When create an invalid user should fail √

The report will look like:

Page 45: Introduction to Software Testing

Rspec (suite per setup)describe('UserService test suite:', function(){ describe("when create a valid user ", function() { beforeEach(function(){ // setup and exercise })

it('should return valid user', function(){ // partial assertions })

it('should call validator', function(){ // partial assertions })

it('should call dao', function(){ // partial assertions })

afterEach(function(){ // clean the state })

})})

Page 46: Introduction to Software Testing

BDD (specification)Feature: User registrationScenario: User tries to register sending valid data so the system will create new account Given the user has introduced <username> and <password> into the registration form And has accepted terms and agreements When send the registration from Then the user with <username> should be createdExample: | username | password | | myNick | p4ssw0rd |

Scenario: User tries to register sending invalid data so the system will reject user Given the user has introduced <username> and <password> into the registration form And has accepted terms and agreements When send the registration from Then the system should notify <error>Example: | username | password | error | | myNick | p4ss | password should have at least 6 characters | | myNick | l4rg3p4ssword | password should have at less than 10 characters | | myNick | password | password should contains at least a number | | | p4ssword | username is mandatory |

Page 47: Introduction to Software Testing

BDD(implementation)@given("the user has introduced (\w)+ and (\w)+ into the registration form")public void populateForm(String username, String password) { ...}

@given("has accepted terms and agreements")public void acceptTerms() { ...}

@when("send the registration from")public void sendRegistrationForm() { ...}

@then("the user with (\w)+ should be created")public void verifyUserIsCreated(String username) { ...}

@then("the system should notify <error>")public void verifyErrors(String error) { ...}

Page 48: Introduction to Software Testing

TESTABLE DESIGN

Page 49: Introduction to Software Testing

Non-Testable design smells (by Misko Hevery*)

•Constructors does Real Work•Digging into collaborators•Brittle Global State & Singletons•Class Does Too Much Work

*See http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

Page 50: Introduction to Software Testing

Constructors does Real Work

• new keyword in a constructor or at field declaration • Static method calls in a constructor or at field declaration • Anything more than field assignment in constructors • Object not fully initialized after the constructor finishes

(watch out for initialize methods) • Control flow (conditional or looping logic) in a

constructor • CL does complex object graph construction inside a

constructor rather than using a factory or builder • Adding or using an initialization block

Page 51: Introduction to Software Testing

Digging into collaborators

• Objects are passed in but never used directly (only used to get access to other objects) • Law of Demeter violation: method call chain

walks an object graph with more than one dot (.) • Suspicious names: context, environment,

principal, container, or manager

Page 52: Introduction to Software Testing

Brittle Global State & Singletons

• Adding or using singletons • Adding or using static fields or static

methods • Adding or using static initialization blocks • Adding or using registries • Adding or using service locators

Page 53: Introduction to Software Testing

Class Does Too Much Work

• Summing up what the class does includes the word “and” • Class would be challenging for new team

members to read and quickly “get it” • Class has fields that are only used in some

methods • Class has static methods that only operate on

parameters

Page 54: Introduction to Software Testing

Questions & Stupid questions• ¿Where I place my tests?• ¿Who tests the classes which test our classes?• ¿Could you be able to rewrite the code only reading the tests

definitions?• I spend more time writing code to setup my SUT than writing

the test, how do you solve it?• ¿What is the minimum coverage should I expect for my code?• I’ve never write a test ¿where can I start?• My code is not testable at all, ¿what can I do?

Page 55: Introduction to Software Testing

¿Where I place my tests?

• Unit tests:• Test Class per Class• Test Class per SetUp (useful in Xunit frameworks)• Important naming convention (<ClassName>Test,

<TestSuite>IntegrationTest, …)• System tests:• Different project

Page 56: Introduction to Software Testing

¿Where I place my tests?Java Project (Test Class per Class) MyProject/ src/ main/ java/ com.groupId.artifactId.MyClass.java resources/ test/ java/ com.groupId.artifactId.MyClassTest.java com.groupId.artifactId.it.suite.MyTestCaseIntegrationTest.java resources/

NodeJs Project MyProject/ lib/ myClass.js main.js test/ ut/ /suite it/ lib/ myClassTest.js

Java Project (Class per SetUp) MyProject/ src/ main/ … test/ java/ com.groupId.artifactId.myclass.<SetUp1>Test.java com.groupId.artifactId.myclass.<SetUp2>Test.java …

Page 57: Introduction to Software Testing

¿Where I place my tests?Android Project MyProject/ AndroidManifest.xml res/ ... (resources for main application) src/ ... (source code for main application) ... tests/ AndroidManifest.xml res/ ... (resources for tests) src/ ... (source code for tests)

IOS ProjectMyIOSProject/ MyIOSProject/ ... app code ... MyIOSProjectTests/ ... test code ...

Page 58: Introduction to Software Testing

¿Who tests the classes which test our classes?

• Exactly, this is why it’s so important our tests follow

KISS

Page 59: Introduction to Software Testing

¿Could you be able to rewrite the code only reading the tests definitions?

• Tests (specially Black Box tests) should tell us an story.

• Use descriptive name methods for unit tests:

• User well defined, and complete scenarios for system tests:• Use business vocabulary for acceptance tests:

public void testValidaterUser1 { ... }

VSpublic void validateUserWithNoPasswordShouldThrowsError { ... }

com.mycompany.artifactId.it.TestSteps ...

VScom.mycompany.artifactId.it.usermanagement.UserCreationSteps ...

Page 60: Introduction to Software Testing

I spend more time writing code to setup my SUT than writing the test, how do you solve it?

• Read about Fixtures (Xunit Patterns is a good reference)• Fresh fixtures• Shared fixtures• Persistent fixtures

Page 61: Introduction to Software Testing

I duplicate too much code on objects creation, mocks definition and assertion…

• Writing a lot of code to initialize value objects?• Create DataBuilders

• Writing a lot of code to initialize mock/stub objects?• Create MockBuilders

• Writing a lot of asserts (more purist says only one assertion)?• Create CustomAsserts

User user = userDataBuilder.createValidUser();

VSUser user = new User("irrelevantUsername", "v4l1dP4ss", [email protected]", ...);

assertNotNull(user.getUsername());assertNotNull(user.getPassword());

assertNotNull(user.getEmail());...

VSassertContainsAllMandatoryData(user);

Page 62: Introduction to Software Testing

¿What is the minimum coverage should I expect for my code?• It depends on the project.• “… test as little as possible to reach a given level

of confidence …”• Do not get obsess over test coverage, it’s a

metric, not a goal.

Page 63: Introduction to Software Testing

I’ve never write a test ¿where can I start?

Database

PORT1 PORT2

ADAPTER1

I’ll bet you a beer , you

called it *Util…

Page 64: Introduction to Software Testing

My code is not testable at all, ¿what can I do?• First of all, go to http://refactoring.com/• I suggest:

1. Add integration regression test.2. Remove new from methods and ad it to constructors (this will

prepare your class for dependency injection).3. Creates a constructor which receive every dependency your

class will need.4. Remove static classes and methods (adding the new non-static

as a class dependency).5. Add as much tests as you want to ;)

Important!!! Do it step by step

Page 66: Introduction to Software Testing

Q&APlace your question here!