Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace Modules\Exercise03\Tests\Unit\Controllers;

use Illuminate\Http\JsonResponse;
use Modules\Exercise03\Http\Controllers\ProductController;
use Modules\Exercise03\Http\Requests\CheckoutRequest;
use Modules\Exercise03\Services\ProductService;
use Tests\TestCase;

class ProductControllerTest extends TestCase
{
/**
* @var \Mockery\MockInterface
*/
protected $productServiceMock;

protected function setUp(): void
{
parent::setUp();

// Laravel helper: mock and bind to service container
$this->productServiceMock = $this->mock(ProductService::class);
}

/**
* A basic unit test example.
*
* @return void
*/
public function test__contruct()
{
$controller = new ProductController($this->productServiceMock);

$this->assertInstanceOf(ProductController::class, $controller);

}
Comment on lines +26 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test constructor có ý nghĩa gì không em?


public function test_index()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test index là test cái gì, tên test case nên thể hiện được mục đích của test case

Mục 1 trong checklist:

{
$this->productServiceMock->shouldReceive('getAllProducts')
->once()
->andReturn([]);
$url = action([ProductController::class, 'index']);

$response = $this->get($url);

$response->assertViewIs('exercise03::index');
$response->assertViewHas('products');
}

public function test_checkout()
{
$input = [
1 => 2,
2 => 3,
3 => 3,
];

$request = $this->mock(CheckoutRequest::class);
$request->shouldReceive('input')
->once()
->with('total_products')
->andReturn($input);

$this->productServiceMock->shouldReceive('calculateDiscount')
->with($input)
->once()
->andReturn(7);

$controller = new ProductController($this->productServiceMock);

$this->assertInstanceOf(JsonResponse::class, $controller->checkout($request));
Comment on lines +60 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cái này viết theo cách dùng HTTP tests như ở test_index được không? Sao case này lại viết cách khác nhỉ?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Đoạn này nên có thêm assert cho
->json(['discount' => $discount]);
Để xem xem là người dùng có truyền discount đúng cái trả về từ service không

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Modules\Exercise03\Tests\Unit\Requests;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Modules\Exercise03\Http\Requests\CheckoutRequest;
use Tests\TestCase;

class CheckoutRequestTest extends TestCase
{
public function test_rules()
{
$request = new CheckoutRequest();

$this->assertSame([
'total_products' => 'required|array',
'total_products.*' => 'nullable|integer|min:0',
], $request->rules());
}
Comment on lines +12 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


public function test_validation_fails_when_data_empty()
{
$request = new CheckoutRequest();
$validator = Validator::make([], $request->rules());

$this->assertTrue($validator->fails());
}

public function test_validation_success()
{
$request = new CheckoutRequest();
$validator = Validator::make([
'total_products' => [1 => 1, 2 => 2, 3 => 3],
], $request->rules());

$this->assertTrue($validator->passes());
}
}
20 changes: 20 additions & 0 deletions Modules/Exercise03/Tests/Feature/Models/ProductTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Modules\Exercise03\Tests\Feature\Models;

use Modules\Exercise03\Database\Factories\ProductFactory;
use Modules\Exercise03\Models\Product;
use Tests\SetupDatabaseTrait;
use Tests\TestCase;

class ProductTest extends TestCase
{
use SetupDatabaseTrait;

public function test_new_factory()
{
$product = Product::newFactory();

$this->assertInstanceOf(ProductFactory::class, $product);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Tests\Unit\Repositories;

use Modules\Exercise03\Models\Product;
use Modules\Exercise03\Repositories\ProductRepository;
use Tests\TestCase;
use Illuminate\Database\Eloquent\Collection;

class ProductRepositoryTest extends TestCase
{
public function test__construct()
{
$product = new Product();
$repository = new ProductRepository($product);

$this->assertInstanceOf(ProductRepository::class, $repository);
}

public function test_all()
{
$product = new Product();
$repository = new ProductRepository($product);

$this->assertInstanceOf(Collection::class, $repository->all());
}
}
100 changes: 100 additions & 0 deletions Modules/Exercise03/Tests/Unit/Services/ProductServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace Tests\Unit\Services;

use InvalidArgumentException;
use Modules\Exercise03\Repositories\ProductRepository;
use Modules\Exercise03\Services\ProductService;
use Tests\TestCase;

class ProductServiceTest extends TestCase
{
protected $productRepositoryMock;
protected $productService;

protected function setUp(): void
{
parent::setUp();

// Laravel helper: mock and bind to service container
$this->productRepositoryMock = $this->mock(ProductRepository::class);
$this->productService = new ProductService($this->productRepositoryMock);

}

public function test__construct()
{
$this->assertInstanceOf(ProductService::class, $this->productService);
}

public function test_get_all_products()
{
$this->productRepositoryMock
->shouldReceive('all')
->andReturn([]);
$products = $this->productService->getAllProducts();

$this->assertEquals($products, []);
}

/**
* @param $totalProducts
* @param $expectedValue
* @dataProvider provideValidTotalProductsData
* */
public function test_calculate_discount_with_valid_data($totalProducts, $expectedValue)
{
$discount = $this->productService->calculateDiscount($totalProducts);
$this->assertEquals($expectedValue, $discount);
}

public function provideValidTotalProductsData()
Copy link

@namttdh namttdh May 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ở đây thiếu miss mất 1 case.
Giả sử các trường hợp dưới đây không tồn tại thì ra sao?

$totalProducts[Product::CRAVAT_TYPE] 
$totalProducts[Product::OTHER_TYPE]
$totalProducts[Product::WHITE_SHIRT_TYPE]

Ngoài thì thì a cũng để ý các trương logic so sánh >= hiện tại anh chỉ đang test các trường hợp > nhưng đối với các trường hợp = thì đang không test

{
return [
[
[
1 => 2,
2 => 2,
3 => 2,
], 5
],
[
[
1 => 0,
2 => 4,
3 => 4,
], 7
],
[
[
1 => 2,
2 => 3,
3 => 3,
], 12
],
];
}
Comment on lines +40 to +76
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Viết như này đọc khá khó hiểu, ít nhất nên đặt tên cho từng dataset hoặc là tách ra các case riêng biệt

  • Input chỉ bao gồm các số, nhìn như các số ngẫu nhiên, không thể hiện được điều kiện input đầu vào là gì => Các key input nó đã được đặt tên bằng const trong class Product thì nên sử dụng ở đây

Với cả thiếu case nhé?

Anh thấy bài này có thể chia case theo kết quả mong muốn thì sẽ rất dễ sinh input, có 4 kết quả mong muốn:

  • Exception => invalid data
  • Discount = 5 => only cravat white discount
  • Discount = 7 => only total quantity discout
  • Discount = 12 => both discounts

Hoặc có thể bám theo lý thuyết C1

        if ($cravat < 0 || $whiteShirt < 0 || $others < 0) {
            // (1)
            throw new InvalidArgumentException();
        } // else (2)

        if ($cravat > 0 && $whiteShirt > 0) {
            // (3)
            $discount = self::CRAVAT_WHITE_SHIRT_DISCOUNT;
        } // else (4)

        if (($cravat + $whiteShirt + $others) >= self::TOTAL_PRODUCT_TO_DISCOUNT) {
            // (5)
            $discount += self::QUANTITY_DISCOUNT;
        } // else (6)
  • (1) => End
  • (2) (3) (5) => End
  • ...

Và nên thêm 1 case để test cận cho điều kiện >= 7, vì dấu >= và dấu > thường rất dễ nhầm


/**
* @param $totalProducts
* @dataProvider provideInvalidTotalProductsData
* */
public function test_exception_calculate_discount($totalProducts)
{
$this->expectException(InvalidArgumentException::class);
$this->productService->calculateDiscount($totalProducts);
}

public function provideInvalidTotalProductsData()
{
return [
[
[
1 => -1,
2 => 2,
3 => 2,
],
],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Modules\Exercise04\Tests\Feature\Http\Controllers;

use Modules\Exercise04\Http\Controllers\CalendarController;
use Modules\Exercise04\Services\CalendarService;
use Tests\TestCase;

class CalendarControllerTest extends TestCase
{
/**
* @var \Mockery\MockInterface
*/
protected $calendarServiceMock;

protected function setUp(): void
{
parent::setUp();

// Laravel helper: mock and bind to service container
$this->calendarServiceMock = $this->mock(CalendarService::class);
}

/**
* A basic unit test example.
*
* @return void
*/
public function test__contruct()
{
$controller = new CalendarController($this->calendarServiceMock);

$this->assertInstanceOf(CalendarController::class, $controller);
}

public function test_index()
{
$this->calendarServiceMock->shouldReceive('getDateClass')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trong controller này có logic, nên phải làm sao test đưuọc cả logic đấy. Muốn test được logic đấy thì có 2 cách
1 là cái mock này không nên chỉ shouldReceiveandReturn luôn.
Mình nên làm để biết thêm là cái method getDateClass gọi bao nhiêu lần và những tham số truyền vào là gì
2 là
$response->assertViewHas('calendars'); thì mình phải assert xem cả cái data của nó nữa

->andReturn(CalendarService::COLOR_BLUE);
$url = action([CalendarController::class, 'index']);

$response = $this->get($url);

$response->assertViewIs('exercise04::calendar');
$response->assertViewHas('calendars');
}
}
54 changes: 54 additions & 0 deletions Modules/Exercise04/Tests/Unit/Services/CalendarServiceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Tests\Unit\Services;

use Carbon\Carbon;
use Modules\Exercise04\Services\CalendarService;
use Tests\TestCase;

class CalendarServiceTest extends TestCase
{
protected $calendarService;

protected function setUp(): void
{
parent::setUp();

$this->calendarService = new CalendarService();
}

/**
* @param $date
* @param $expectedValue
* @dataProvider provideData
* */
public function test_get_date_class($date, $expectedValue)
{
$holidays = ['2021-05-19'];

$class = $this->calendarService->getDateClass($date, $holidays);
$this->assertEquals($expectedValue, $class);
}

public function provideData()
{
return [
'Normal_day' => [
Carbon::createFromDate(2021, 05, 18),
CalendarService::COLOR_BLACK
],
'Holiday' => [
Carbon::createFromDate(2021, 05, 19),
CalendarService::COLOR_RED
],
'Saturday' => [
Carbon::createFromDate(2021, 05, 22),
CalendarService::COLOR_BLUE
],
'Sunday' => [
Carbon::createFromDate(2021, 05, 23),
CalendarService::COLOR_RED
],
];
}
}
Loading