Skip to main content

Testing

Testing is essential for building reliable, scalable, and maintainable systems.

Overview

Software Testing evaluates and verifies that software works correctly, meets requirements, and is free from defects.

Without testing: bugs reach production, systems become unstable, deployments become risky.

With proper testing: faster development, safer refactoring, better architecture, continuous delivery becomes possible.


Types of Testing

TypePurpose
Unit TestingTest isolated components
Integration TestingTest component interaction
Functional TestingValidate business functionality
End-to-End TestingSimulate user workflows
Regression TestingPrevent old bugs from returning
Smoke TestingValidate critical functionality
Performance TestingMeasure speed and scalability
Load TestingSimulate high traffic
Stress TestingTest breaking points
Security TestingFind vulnerabilities
Acceptance TestingValidate against business requirements

Testing Pyramid

LevelCharacteristics
Unit TestsFast, cheap, highly isolated, most numerous
Integration TestsValidate communication between modules
End-to-End TestsExpensive, slow, closest to real user behavior

Testing Lifecycle


Unit Testing

AAA Pattern (Arrange, Act, Assert)

[Fact]
public void Withdraw_ShouldReduceBalance()
{
// Arrange
var account = new BankAccount(100);

// Act
account.Withdraw(40);

// Assert
Assert.Equal(60, account.Balance);
}

Test-Driven Development (TDD)

Benefits: Better design, lower coupling, safer refactoring, high confidence.


Integration Testing

Integration testing validates communication between: APIs, databases, external services, queues, microservices.

public class UserServiceTests
{
[Fact]
public async Task CreateUser_ShouldPersistData()
{
var dbContext = CreateTestDbContext();
var service = new UserService(dbContext);

await service.CreateUserAsync("John");

Assert.Equal(1, dbContext.Users.Count());
}
}

End-to-End Testing (E2E)

E2E tests simulate real user workflows. Example flow:

  1. Open website
  2. Login
  3. Add item to cart
  4. Checkout
  5. Verify order confirmation

Selenium Example

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

var driver = new ChromeDriver();

driver.Navigate().GoToUrl("https://example.com");

driver.FindElement(By.Id("username")).SendKeys("admin");
driver.FindElement(By.Id("password")).SendKeys("123456");
driver.FindElement(By.Id("login")).Click();

Mocking

Mocks simulate dependencies during testing.

Moq Example

var repositoryMock = new Mock<IUserRepository>();

repositoryMock.Setup(x => x.GetUser(1))
.Returns(new User { Name = "John" });

var service = new UserService(repositoryMock.Object);

var result = service.GetUser(1);

Assert.Equal("John", result.Name);

Code Coverage

TypeDescription
Line CoverageExecuted lines
Branch CoverageExecuted branches
Path CoverageExecuted paths

CI/CD Testing Pipeline


Performance Testing

Load Testing vs Stress Testing

Load TestingStress Testing
Normal expected trafficExtreme traffic
Measure stabilityFind a breaking point
Capacity planningFailure analysis

API Testing

Validations: status codes, response schema, authentication, authorization, response time, error handling.

[Fact]
public async Task GetUser_ShouldReturn200()
{
var response = await _client.GetAsync("/api/users/1");

Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

Security Testing

Focuses on: authentication, authorization, SQL injection, XSS, CSRF, secrets exposure.


Common Testing Tools

CategoryTools
Unit TestingxUnit, NUnit, MSTest
MockingMoq, NSubstitute
API TestingPostman, RestAssured
UI TestingSelenium, Playwright
Load TestingJMeter, k6
CoverageCoverlet, JaCoCo
CI/CDGitHub Actions, Jenkins

Testing in Microservices

Challenges: distributed systems, network failures, service dependencies, eventual consistency.

Strategies: contract testing, consumer-driven tests, service virtualization.

Contract Testing


Best Practices

  • Write small tests — easier debugging and better readability
  • Keep tests deterministic — avoid random values, time dependencies, external systems
  • Test behavior, not implementation — focus on outcomes
  • Use meaningful names: CalculateTotal_ShouldReturnCorrectPrice_WhenDiscountApplied

Common Pitfalls

PitfallProblem
Too many E2E testsSlow pipeline
Shared test stateFlaky tests
Testing implementation detailsFragile tests
Ignoring edge casesProduction bugs
No automationManual overhead

Flaky Tests

Causes: timing issues, shared state, race conditions, external dependencies.


Testing Strategy by Project Size

SizeStrategy
Small ProjectsMostly unit tests, minimal integration tests
Medium ProjectsUnit + integration, some E2E
Enterprise SystemsFull testing pyramid, automation pipelines, security testing, performance testing, chaos engineering

Chaos Engineering

Intentionally introduces failures (server crashes, network latency, database failures) to improve resilience.


Performance Optimization for Tests

// Use in-memory databases
UseInMemoryDatabase("TestDb");
  • Run tests in parallel
  • Avoid real network calls — mock external APIs
  • Use test containers for integration tests

Enterprise Testing Workflow


src/
tests/
unit/
integration/
e2e/

Interview Questions

Basic

  1. What is unit testing?
  2. What is mocking?
  3. Explain the testing pyramid.
  4. Difference between integration and E2E testing?

Intermediate

  1. What makes a good test?
  2. What causes flaky tests?
  3. Explain TDD.
  4. How do you test APIs?

Advanced

  1. How do you test microservices?
  2. Explain contract testing.
  3. How would you design enterprise testing pipelines?
  4. Strategies for resilient distributed systems?

Testing Cheat Sheet

ConceptSummary
Unit TestIsolated component test
Integration TestMultiple components
E2E TestFull workflow
MockFake dependency
StubPredefined response
FakeSimplified implementation
SpyTracks interactions
Regression TestPrevent reintroduced bugs