diff --git a/src/main/java/com/nisum/reactiveweb/controller/ProductHandler.java b/src/main/java/com/nisum/reactiveweb/controller/ProductHandler.java
index 7ede2e419b5aa1c9a2c2a66abf7082673eb26b7a..f27ec95a1b21cb7c8c4434108a1e852422dfc623 100644
--- a/src/main/java/com/nisum/reactiveweb/controller/ProductHandler.java
+++ b/src/main/java/com/nisum/reactiveweb/controller/ProductHandler.java
@@ -8,8 +8,8 @@ import java.util.stream.Collectors;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
 import org.springframework.context.support.DefaultMessageSourceResolvable;
-import org.springframework.stereotype.Component;
 import org.springframework.util.Assert;
 import org.springframework.validation.BeanPropertyBindingResult;
 import org.springframework.validation.Errors;
@@ -24,7 +24,7 @@ import static org.springframework.web.reactive.function.server.ServerResponse.cr
 import static org.springframework.web.reactive.function.server.ServerResponse.ok;
 
 @Slf4j
-@Component
+@Configuration
 public class ProductHandler {
 
     @Autowired
@@ -57,7 +57,6 @@ public class ProductHandler {
                                      .flatMap(product -> productService.updateProduct(req.pathVariable(SKU_TEXT), product))
                                      .flatMap(product -> accepted()
                                              .bodyValue(product))
-
                         )
                         .DELETE(SKU_TEXT_PATH_VAR,
                                 req -> productService.removeProduct(req.pathVariable(SKU_TEXT))
diff --git a/src/main/java/com/nisum/reactiveweb/exceptions/ApiError.java b/src/main/java/com/nisum/reactiveweb/exceptions/ApiErrorAttributes.java
similarity index 96%
rename from src/main/java/com/nisum/reactiveweb/exceptions/ApiError.java
rename to src/main/java/com/nisum/reactiveweb/exceptions/ApiErrorAttributes.java
index 2cbb476d9f9d8100d07154ef9fb368c8bf5e15bc..ddede4875d6b6e70b703f47543f630c2b57026de 100644
--- a/src/main/java/com/nisum/reactiveweb/exceptions/ApiError.java
+++ b/src/main/java/com/nisum/reactiveweb/exceptions/ApiErrorAttributes.java
@@ -17,7 +17,7 @@ import static org.springframework.http.HttpStatus.NOT_FOUND;
 
 @Component
 @Slf4j
-public class ApiError extends DefaultErrorAttributes {
+public class ApiErrorAttributes extends DefaultErrorAttributes {
 
     @Override
     public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
diff --git a/src/main/java/com/nisum/reactiveweb/repository/ProductRepository.java b/src/main/java/com/nisum/reactiveweb/repository/ProductRepository.java
index b936ddaeb43da811efb892c898129fbc8f5c2135..2e7e18e0ca84b2aa92bdd1374db62cc9d8ef0d30 100644
--- a/src/main/java/com/nisum/reactiveweb/repository/ProductRepository.java
+++ b/src/main/java/com/nisum/reactiveweb/repository/ProductRepository.java
@@ -14,4 +14,6 @@ public interface ProductRepository {
     Mono<Product> updateProduct(String sku, Product product);
 
     Mono<Long> removeProduct(String sku);
+
+    Mono<Long> removeAllProducts();
 }
diff --git a/src/main/java/com/nisum/reactiveweb/repository/ProductRepositoryImpl.java b/src/main/java/com/nisum/reactiveweb/repository/ProductRepositoryImpl.java
index af0384bfeebc35711fd13b9de94746867ccd32a6..2f45e9870638f3c96ae6fb423b0b6d966b2db8af 100644
--- a/src/main/java/com/nisum/reactiveweb/repository/ProductRepositoryImpl.java
+++ b/src/main/java/com/nisum/reactiveweb/repository/ProductRepositoryImpl.java
@@ -54,4 +54,9 @@ public class ProductRepositoryImpl implements ProductRepository {
                 .map(DeleteResult::getDeletedCount);
     }
 
+    @Override
+    public Mono<Long> removeAllProducts() {
+        return template.remove(new Query(), Product.class)
+                .map(DeleteResult::getDeletedCount);
+    }
 }
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3345aff2b85617dd13b468bc882cd4a28518b5e5
--- /dev/null
+++ b/src/main/resources/application-test.yml
@@ -0,0 +1,6 @@
+spring:
+  data:
+    mongodb:
+      host: localhost
+      port: 27017
+      database: reactive-test
\ No newline at end of file
diff --git a/src/test/java/com/nisum/reactiveweb/ReactiveWebApplicationTests.java b/src/test/java/com/nisum/reactiveweb/ReactiveWebApplicationTests.java
index e664cb12d8ce29829974c88de4236f8142d5d3e9..9d749439b1f4aa1a19921629e24f0344613fc514 100644
--- a/src/test/java/com/nisum/reactiveweb/ReactiveWebApplicationTests.java
+++ b/src/test/java/com/nisum/reactiveweb/ReactiveWebApplicationTests.java
@@ -1,13 +1,7 @@
 package com.nisum.reactiveweb;
 
-import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
 @SpringBootTest
 class ReactiveWebApplicationTests {
-
-	@Test
-	void contextLoads() {
-	}
-
 }
diff --git a/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerComponentTests.java b/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerComponentTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..78ecb7b04025f699937e73885709d2388d1edfd8
--- /dev/null
+++ b/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerComponentTests.java
@@ -0,0 +1,189 @@
+package com.nisum.reactiveweb.controller;
+
+import com.nisum.reactiveweb.exceptions.ApiErrorAttributes;
+import com.nisum.reactiveweb.exceptions.ProductNotFoundException;
+import com.nisum.reactiveweb.model.Product;
+import com.nisum.reactiveweb.service.ProductServiceImpl;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.math.BigDecimal;
+import java.util.function.Predicate;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.when;
+import static reactor.core.publisher.Mono.just;
+
+@WebFluxTest()
+@Import({ProductHandler.class, ApiErrorAttributes.class})
+class ProductHandlerComponentTests {
+
+    @Autowired
+    private WebTestClient testClient;
+
+    @MockBean
+    private ProductServiceImpl productService;
+
+    private final Product product1 = new Product("1", "toy", new BigDecimal(200));
+
+    private final Product product3 = new Product("1", "toy", new BigDecimal(300));
+
+    private final Product product2 = new Product("2", "table", new BigDecimal(400));
+
+    private final Predicate<Product> productPredicate1 = productPredicate("1", "toy", 200);
+
+    private final Predicate<Product> productPredicate2 = productPredicate("1", "toy", 300);
+
+    private final Mono<Product> productNotFoundErrorMono = Mono.error(ProductNotFoundException::new);
+
+    @Test
+    void getProductBySku() {
+        when(productService.getProductBySku("1")).thenReturn(productMono());
+        testClient.get()
+                .uri("/product/1")
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(Product.class).value(Matchers.equalTo(product1));
+    }
+
+    @Test
+    void getProductBySkuWhenProductDoesNotExist() {
+        when(productService.getProductBySku("1")).thenReturn(productNotFoundErrorMono);
+        testClient.get()
+                .uri("/product/1")
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    void saveProduct() {
+        Product productToSave = new Product(null, "toy", new BigDecimal(200));
+        when(productService.saveProduct(productToSave)).thenReturn(productMono());
+        testClient.post()
+                .uri("/product")
+                .body(just(productToSave), Product.class)
+                .exchange()
+                .expectStatus().isCreated()
+                .expectBody(Product.class).isEqualTo(product1)
+                .consumeWith(result -> {
+                    Product savedProduct = result.getResponseBody();
+                    assertNotNull(savedProduct);
+                    assertNotNull(savedProduct.getSku());
+                    assertTrue(productPredicate1.test(savedProduct));
+                });
+    }
+
+    @Test
+    void saveProductWithProductNameOrPriceAsNull() {
+        Product productToSave = new Product();
+        testClient.post()
+                .uri("/product")
+                .body(just(productToSave), Product.class)
+                .exchange()
+                .expectStatus().isBadRequest();
+    }
+
+    @Test
+    void updateProduct() {
+        Product productToUpdate = new Product(null, "toy", new BigDecimal(300));
+        String sku = "1";
+        when(productService.updateProduct(sku, productToUpdate)).thenReturn(Mono.just(product3));
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isAccepted()
+                .expectBody(Product.class).isEqualTo(product3)
+                .consumeWith(result -> {
+                    Product updatedProduct = result.getResponseBody();
+                    assertNotNull(updatedProduct);
+                    assertEquals(sku, updatedProduct.getSku());
+                    assertEquals(product3.getPrice(), updatedProduct.getPrice());
+                    assertTrue(productPredicate2.test(updatedProduct));
+                });
+    }
+
+    @Test
+    void updateProductWhenProductNameOrPriceIsNull() {
+        Product productToUpdate = new Product();
+        String sku = "5";
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isBadRequest();
+    }
+
+    @Test
+    void updateProductWhenProductDoesNotExistWithGivenSku() {
+        Product productToUpdate = new Product(null, "toy", new BigDecimal(300));
+        String sku = "5";
+        when(productService.updateProduct(sku, productToUpdate)).thenReturn(productNotFoundErrorMono);
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    void removeProduct() {
+        String sku = "5";
+        final String deletedText = "deleted";
+        when(productService.removeProduct(sku)).thenReturn(Mono.just(deletedText));
+        testClient.delete()
+                .uri("/product/" + sku)
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(String.class)
+                .consumeWith(result -> assertEquals(deletedText, result.getResponseBody()));
+    }
+
+    @Test
+    void removeProductWhenSkuDoesNotExist() {
+        String sku = "5";
+        when(productService.removeProduct(sku)).thenReturn(Mono.error(ProductNotFoundException::new));
+        testClient.delete()
+                .uri("/product/" + sku)
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    void getAllProducts() {
+        when(productService.getAllProducts()).thenReturn(productsFlux());
+
+        testClient.get().uri("/products")
+                .exchange()
+                .expectStatus().isOk()
+                .expectBodyList(Product.class)
+                .hasSize(2)
+                .contains(product1, product2);
+
+    }
+
+    private Mono<Product> productMono() {
+        return just(product1);
+    }
+
+    private Flux<Product> productsFlux() {
+        return Flux.just(product1, product2);
+    }
+
+    private Predicate<Product> productPredicate(String sku, String name, int price) {
+        return product -> product.getSku().equals(sku)
+                && product.getName().equals(name)
+                && product.getPrice().intValue() == price;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerIntegrationTests.java b/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerIntegrationTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5fafc7583cc9634626860200ffa6395460f3920
--- /dev/null
+++ b/src/test/java/com/nisum/reactiveweb/controller/ProductHandlerIntegrationTests.java
@@ -0,0 +1,171 @@
+package com.nisum.reactiveweb.controller;
+
+import com.nisum.reactiveweb.model.Product;
+import com.nisum.reactiveweb.repository.ProductRepository;
+
+import java.math.BigDecimal;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static reactor.core.publisher.Mono.just;
+
+@SpringBootTest
+@ActiveProfiles("test")
+@TestMethodOrder(OrderAnnotation.class)
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+@AutoConfigureWebTestClient
+class ProductHandlerIntegrationTests {
+
+    @Autowired
+    private WebTestClient testClient;
+
+    @Autowired
+    private ProductRepository repository;
+
+    private String sku = "";
+
+    @BeforeAll
+    @AfterAll
+    void setup() {
+        repository.removeAllProducts().block();
+    }
+
+    @Test
+    @Order(3)
+    void getProductBySku() {
+        testClient.get()
+                .uri("/product/" + sku)
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(Product.class)
+                .consumeWith(result -> {
+                    Product product = result.getResponseBody();
+                    assertNotNull(product);
+                });
+    }
+
+    @Test
+    void getProductBySkuWhenProductDoesNotExist() {
+        testClient.get()
+                .uri("/product/1")
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    @Order(1)
+    void saveProduct() {
+        Product productToSave = new Product(null, "toy", new BigDecimal(200));
+        testClient.post()
+                .uri("/product")
+                .body(just(productToSave), Product.class)
+                .exchange()
+                .expectStatus().isCreated()
+                .expectBody(Product.class)
+                .consumeWith(result -> {
+                    Product savedProduct = result.getResponseBody();
+                    assertNotNull(savedProduct);
+                    assertNotNull(savedProduct.getSku());
+                    sku = savedProduct.getSku();
+                    assertEquals(productToSave.getName(), savedProduct.getName());
+                    assertEquals(productToSave.getPrice(), savedProduct.getPrice());
+                });
+    }
+
+    @Test
+    void saveProductWithProductNameOrPriceAsNull() {
+        Product productToSave = new Product();
+        testClient.post()
+                .uri("/product")
+                .body(just(productToSave), Product.class)
+                .exchange()
+                .expectStatus().isBadRequest();
+    }
+
+    @Test
+    @Order(2)
+    void updateProduct() {
+        String productName = "toy";
+        BigDecimal productPrice = new BigDecimal(300);
+        Product productToUpdate = new Product(null, productName, productPrice);
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isAccepted()
+                .expectBody(Product.class)
+                .consumeWith(result -> {
+                    Product updatedProduct = result.getResponseBody();
+                    assertNotNull(updatedProduct);
+                    assertEquals(sku, updatedProduct.getSku());
+                    assertEquals(productName, updatedProduct.getName());
+                    assertEquals(productPrice, updatedProduct.getPrice());
+                });
+    }
+
+    @Test
+    void updateProductWhenProductNameOrPriceIsNull() {
+        Product productToUpdate = new Product();
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isBadRequest();
+    }
+
+    @Test
+    void updateProductWhenProductDoesNotExistWithGivenSku() {
+        Product productToUpdate = new Product(null, "toy", new BigDecimal(300));
+        String sku = "5";
+        testClient.put()
+                .uri("/product/" + sku)
+                .body(just(productToUpdate), Product.class)
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    @Order(10)
+    void removeProduct() {
+        final String deletedText = "deleted";
+        testClient.delete()
+                .uri("/product/" + sku)
+                .exchange()
+                .expectStatus().isOk()
+                .expectBody(String.class)
+                .consumeWith(result -> assertEquals(deletedText, result.getResponseBody()));
+    }
+
+    @Test
+    void removeProductWhenSkuDoesNotExist() {
+        String sku = "5";
+        testClient.delete()
+                .uri("/product/" + sku)
+                .exchange()
+                .expectStatus().isNotFound();
+    }
+
+    @Test
+    @Order(3)
+    void getAllProducts() {
+        testClient.get().uri("/products")
+                .exchange()
+                .expectStatus().isOk()
+                .expectBodyList(Product.class)
+                .hasSize(1);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/nisum/reactiveweb/repository/ProductRepositoryTest.java b/src/test/java/com/nisum/reactiveweb/repository/ProductRepositoryTest.java
deleted file mode 100644
index 1f87b93ddabdacab3bcc4c61f9cedd8345e17d0f..0000000000000000000000000000000000000000
--- a/src/test/java/com/nisum/reactiveweb/repository/ProductRepositoryTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.nisum.reactiveweb.repository;
-
-import org.junit.jupiter.api.Test;
-
-class ProductRepositoryTest {
-
-    @Test
-    void getProductBySku() {
-    }
-
-    @Test
-    void getAllProducts() {
-    }
-
-    @Test
-    void saveProduct() {
-    }
-
-    @Test
-    void updateProduct() {
-    }
-
-    @Test
-    void removeProduct() {
-    }
-}
\ No newline at end of file