vue3 콤보 api와 jest 유닛 테스트가 포함된 Laravel 때가 낀 예

117508 단어 vuetypescriptlaravelphp

서점


vue 구성 요소에 vuejs3 전단, 유형 스크립트, 단원 테스트를 포함하는 예시 laravel 프로그램을 만들 것입니다.

1. laravel 프로젝트 설립


새로운 도구로 laravel 프로젝트를 만듭니다.환경 파일
laravel new laravel-online-books && cd laravel-online-books
cp .env.example .env

에반 선생


APP_NAME="Online books"
...
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<YOUR_DB>
DB_USERNAME=<YOUR_DB_USER>
DB_PASSWORD=<YOUR_DB_PASSWORD>

...
composer를 실행하여 데이터베이스 설치 및 이전
composer install
php artisan migrate
laravel/ui 비계와 비계 설정 ui
composer require Laravel/ui     
php artisan ui bootstrap --auth
npm install && npm run dev 
laravel starter 프로그램을 실행합니다
php artisan key:generate
php artisan serve

2. 모형


artisan을 사용하여 이동을 통해 우리의 모형을 생성합니다.
php artisan make:model Book -m
테이블 마이그레이션 만들기
// database/migrations/202x_xxx_create_books_table.php 

public function up()
{
    Schema::create('books', function (Blueprint $table) {
        $table->id();
            $table->string('title');
            $table->integer('year');
            $table->string('genre');
            $table->string('author');
            $table->string('publisher');
            $table->timestamps();
        });
}
public function down()
{
    Schema::dropIfExists('books');
}
도서 모형.
// app/models/book.php
class Book extends Model
{
    use HasFactory;
    protected $fillable = ['title' , 'year', 'genre',                                                   
                           'author','publisher'];
}

3. 데이터베이스에 테스트 데이터 심기


너는 이 부분을 뛰어넘을 수 있지만, 가장 좋은 것은 테스트 데이터가 시작되는 것이다.
php artisan make:seeder BookSeeder
// database/seeders/BookSeeder.php
public function run()
{
    $authors = ['Terry A', 'Steven Price', 'John Smith'];
    $genres = ['Fiction','Non-Fiction','Business','Horror'];
    $publishers = ['Publisher A','Publisher B','Publisher C'];

   for ($i = 0; $i <= 10; $i++) {
      DB::table('books')->insert(
         [
           'title' => "Book title {$i}",
           'year' => rand(1995, 2021),
           'genre' => $genres[rand(0, count($genres)-1)],
           'author' => $authors[rand(0, count($authors)-1)],
           'publisher' => $publishers[rand(0, 
                             count($publishers)-1)]
         ]);
   }
}

응용 프로그램 데이터베이스 파종기에서 테스트 데이터 파종기를 호출하다
// database/seeders/DatabaseSeeder.php

public function run()
{
   $this->call([
       BookSeeder::class
   ]);
}
마지막으로, 이전과 피드 데이터베이스.
php artisan migrate --seed

3. 관제사와 노선


책 컨트롤러를 만듭니다. (이것은 우리의 API입니다.)
php artisan make:controller Api/BookController --resource --api --model=Book
자원을 창조하여 현상을 유지하다.
php artisan make:resource BookResource 
요청을 만들고 규칙 그룹을 업데이트합니다. 아래와 같습니다.
php artisan make:request BookRequest 
// app/Http/Requests/BookRequest.php
public function rules()
{
    return [
        'year' => ['required', 'integer'],
        'title' => ['required', 'string'],
        'genre' => ['required', 'string'],
        'author' => ['required', 'string'],
        'publisher' => ['required', 'string'],
    ];
}

현재 우리의 컨트롤러는 CRUD 조작을 지원한다.
App/http/controllers/api/BookController.php

<?php
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\BookRequest;
use App\Http\Resources\BookResource;
use App\Models\Book;

class BookController extends Controller
{
    /**
     * Get all books
     **/
    public function index()
    {
        return BookResource::collection(Book::all());
    }

    /**
     * Store a book
     **/
    public function store(BookRequest $request)
    {
        $book = Book::create($request->validated());
        return new BookResource($book);
    }

    /**
     * Get one book
     **/
    public function show(Book $book)
    {
        return new BookResource($book);
    }

    /**
     * Update a book
     **/
    public function update(BookRequest $request, Book $book)
    {
        $book->update($request->validated());
        return new BookResource($book);
    }

    /**
     * Delete a book
     **/
    public function destroy(Book $book)
    {
        $book->delete();
        return response()->noContent();
    }
}

마지막으로, 우리는 컨트롤러와 노선을 연결할 것이다.
// App/routes/api.php

use App\Http\Controllers\Api\BookController;

// ... 

Route::apiResource('books', BookController::class);
현재 서버가 실행될 때localhost:8000/api/books를 누르면 이 점을 테스트할 수 있습니다.

4. vue3 프런트엔드 설정


노드 버전 12를 제 설정으로 사용하는 것을 권장합니다.
nvm use v12
vuejs3, vuejs3 로더, vue- 설치router@next타자 원고
npm install --save vue@next vue-router@next vue-loader@next
npm install typescript ts-loader --save-dev
typescript를 설정합니다. 전단 모듈에서 typescript를 사용할 것입니다.
tsconfig를 생성합니다.json
/* tsconfig.json */ 

{
    "compilerOptions":
    {
        "module": "commonjs",
        "strict": true,
        "jsx": "preserve",
        "moduleResolution": "node"
    }
}
패드를 추가합니다.typescript가 우리의 vue 파일을 이해할 수 있도록 d,ts 파일입니다.
// resources/shims-vue.d.ts
declare module '*.vue' {
    import type { DefineComponent } from 'vue'
    const component: DefineComponent<{}, {}, any>
    export default component
 }
응용 프로그램에 vue loader 사용
// webpack.mix.js

const mix = require('laravel-mix');
mix.ts('resources/js/app.ts', 'public/js')
    .vue()
    .sass('resources/sass/app.scss', 'public/css')
    .sourceMaps();
이제 vue 공유기 루트를 설정합니다.
라우터/인덱스를 만듭니다.ts
// resources/js/router/index.ts

import { createRouter, createWebHistory } from 'vue-router';

import BookIndex from '../components/books/BookIndex.vue';
import BookShow from '../components/books/BookShow.vue';

const routes = [
    {
        path: '/home',
        name: 'books.index',
        component: BookIndex
    },
    {
        path: '/books/:id/show',
        name: 'books.show',
        component: BookShow,
        props: true
    },
    { path: "/:pathMatch(.*)", component: { template: "Not found"} }
];

export default createRouter({
    history: createWebHistory(),
    routes
});
이제 구성 요소를 만듭니다.
도서 색인.vue - 현재 우리는 그것을 비워 두고 설정에 중점을 둘 것이다
// resources/js/components/BookIndex.vue

<template>
    <div class="container">
        empty
    </div>
</template>
도서 색인.vue - 현재 우리는 그것을 비워 두고 설정에 중점을 둘 것이다
구성 요소 만들기
// resources/js/components/BookIndex.vue

<template>
    <div class="container">
        empty
    </div>
</template>
vue 프로그램을 설치합니다. 확장을 확인하십시오.ts
// resources/js/app.ts
require('./bootstrap');

import { createApp } from "vue";
import router from './router';
import BookIndex from './components/books/BookIndex.vue';

const app = createApp({
    components: {
        BookIndex,
    },
}).use(router).mount('#app')

laravel 루트는 vue 공유기를 사용하여 URL 모드와 일치하는지/홈 페이지를 알려 줍니다.
// routes/web.php
...

Route::view('/{any}', 'home')
    ->middleware(['auth'])
    ->where('any', '.*');
...
기본 블레이드 파일에 라우터 뷰 추가
로그인한
...
   <router-view />
...

우리 모두 잘 지내고 있으니 빨리 뛰어라
npm run dev
그리고 신청서 다시 테스트해 봐.

6.get 구성 요소 추가


우선, 우리는 조합할 수 있는 책을 추가할 것이다.우리는 여기에 모든api 논리를 저장할 것입니다.
자원/js/composables/books를 만듭니다.ts
// resources/js/composables/books.ts

import { ref } from 'vue'
import axios from "axios";

export default function useBooks() {
    const books = ref([])
    const book = ref([])

    const getBooks = async () => {
        const response = await axios.get('/api/books');
        books.value = response.data.data;
    }

    const getBook = async (id: number) => {
        let response = await axios.get('/api/books/' + id)
        book.value = response.data.data;
    }

    return {
        books,
        book,
        getBook,
        getBooks
    }
}
구성 요소 업데이트
// resources/js/components/BookIndex.vue

<template>
    <div class="container">
        <div class="card" style="width: 18rem; float: left; margin: 5px" v-for="book in books" :key="book.id">
            <router-link :to="{ name: 'books.show' , params: { id: book.id }}">
                <div class="card-body">
                        <h5 class="card-title text-center">{{ book.title}}</h5>
                        <h6 class="card-subtitle mb-2 text-muted text-center">{{ book.year}}</h6>
                        <h6 class="card-subtitle mb-2 text-muted text-center">Author: {{ book.author}}</h6>
                        <h6 class="card-subtitle mb-2 text-muted text-center">Pblisher: {{ book.publisher}}</h6>
                        <h6 class="card-subtitle mb-2 text-muted text-center">Genre: {{ book.genre}}</h6>
                </div>
            </router-link>
        </div>
    </div>
</template>

<script lang='ts'>
import useBooks from '../../composables/books';
import { onMounted } from 'vue';

export default {
    setup() {
        const { books, getBooks } = useBooks()
        onMounted(getBooks)

        return {
            books
        }
    }
}
</script>
// resources/js/components/BookShow.vue

<template>
   <div class="container">
       <div>
            <h2 class="card-subtitle mb-2 text-muted">{{ book.title}}</h2>
            <h6 class="card-subtitle mb-2 text-muted">year: {{ book.year}}</h6>
            <h6 class="card-subtitle mb-2 text-muted">written by: {{ book.author}}</h6>
            <h6 class="card-subtitle mb-2 text-muted">published by: {{ book.publisher}}</h6>
            <h6 class="card-subtitle mb-2 text-muted">genre: {{ book.genre}}</h6>
       </div>
       <div class="row">
           <div class="col-12 border">
               <div class="card" style="width: 100%;  margin: 10px; padding: 10px;" v-for="page in 10" :key="page">
                   <div class="card-body">
                           Lorem ipsum dolor sit amet, consectetur adipiscing elit. In varius facilisis dolor,
                           at porttitor nunc luctus sit amet. In tincidunt orci id mi finibus dapibus. Proin tempus,
                           lorem eu dapibus luctus, elit ante facilisis nulla, ac tristique augue justo eu turpis.
                           Donec eu enim a sem malesuada vulputate. In at placerat ex. Nullam tincidunt dolor et magna condimentum,
                            eu pulvinar lorem dictum. Phasellus venenatis rutrum imperdiet. Aenean eu massa lobortis, condimentum nunc sed,
                            molestie sem. Integer a interdum libero. Suspendisse mollis vehicula ligula a feugiat. Curabitur non odio sit amet mi
                            condimentum iaculis. Fusce sed tincidunt sem. Aenean porta viverra neque tristique ultricies.
                   </div>
               </div>
           </div>
       </div>
   </div>
</template>

<script lang='ts'>
import useBooks from '../../composables/books';
import { onMounted } from 'vue';

export default {
   props: {
       id: {
           required: true,
           type: String
       }
   },

   setup(props: any) {
       const { book, getBook } = useBooks()

       onMounted(() => getBook(props.id))

       return {
           book
       }
   }
}
</script>   

mix 재실행 및 응용 프로그램 테스트

7.vue 구성 요소 테스트 추가


테스트를 실행하려면 다음과 같은 설정이 필요합니다.
  • 농담
  • Vue jest 및 babel jest
  • ts농담
  • vue 테스트 -utils@3
  • jest 설치 및 test cmd 추가
    npm install jest --save-dev
    
    // jest.config.js
    
    module.exports = {
      testRegex: 'resources/assets/js/test/.*.spec.js$'
    }
    
    // package.json
    
    scripts : {
        ...
        "test": "jest"
    }
    
    vue jest 및 babel jest 추가:
    vue jest: @vue/vue3 jest for jest 27 및 vuejs3
    npm install --save-dev @vue/vue3-jest babel-jest
    
    vue-js-test-utils-3:
    npm install --save-dev @vue/test-utils@next
    
    typescript로 테스트를 작성할 수 있도록 아래 내용을 추가합니다
    ts jest 및 @types\jest:
    npm install --save-dev ts-jest
    npm install --save-dev @types/jest
    
    jest 구성 업데이트
    // jest.config.js
    
    module.exports = {
        "testEnvironment": "jsdom",
        testRegex: 'resources/js/tests/.*.spec.ts$',
        moduleFileExtensions: [
          'js',
          'json',
          'vue',
          'ts'
        ],
        'transform': {
          '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
          '.*\\.(vue)$': '@vue/vue3-jest',
          "^.+\\.tsx?$": "ts-jest"
        },
      }
    
    구성 요소 테스트 작성
    //resources/js/tests/components/books/BookIndex.spec.ts
    
    import { mount, shallowMount, flushPromises } from "@vue/test-utils";
    import BookIndex from "../../../components/books/BookIndex.vue";
    import router from "../../../router";
    import axios from 'axios';
    
    jest.mock('axios');
    const mockedAxios = axios as jest.Mocked<typeof axios>;
    
    const fakeBooks = [{ "id": "1", "title": "book1", "subtitle": "hello1", "year": 1938}, { "id": "1", "title": "book1", "subtitle": "hello1", "year": 1938}, { "id": "1", "title": "book1", "subtitle": "hello1", "year": 1938}];
    const fakeData = Promise.resolve({"data": {"data": fakeBooks}});
    
    describe("BookIndex.vue", () => {
    
        beforeEach(() => {
        })
    
      it("correctly mounts with correct data", async () => {
    
        mockedAxios.get.mockReturnValueOnce(fakeData);
    
        const wrapper = shallowMount(BookIndex, {
          global: {
            plugins: [router],
          }
        } as any);
    
        expect(axios.get).toBeCalledWith("/api/books");
    
        await flushPromises();
        expect(wrapper.vm.books.length).toBe(3);
      });
    
    });
    
    //resources/js/tests/components/books/BookShow.spec.ts
    
    import { mount, shallowMount, flushPromises } from "@vue/test-utils";
    import BookShow from "../../../components/books/BookShow.vue";
    import router from "../../../router";
    import axios from 'axios';
    
    jest.mock('axios');
    const mockedAxios = axios as jest.Mocked<typeof axios>;
    
    const testId = '3';
    const fakeBook = { "id": "3", "title": "book1", "subtitle": "hello1", "year": 1938}
    const fakeData = Promise.resolve({"data": fakeBook});
    
    
    describe("BookShow.vue", () => {
    
        beforeEach(() => {
        })
    
      it("correctly mounts with correct data", async () => {
    
        mockedAxios.get.mockReturnValueOnce(fakeData);
    
        const wrapper = shallowMount(BookShow, {
        propsData: {
            id: testId
        },
          global: {
            plugins: [router],
          }
        } as any);
    
        expect(axios.get).toBeCalledWith("/api/books/"+testId);
    
        await flushPromises();
        expect(wrapper.vm.book.id).toBe(testId);
      });
    
    });
    
    
    더 많은 테스트를 작성할 수 있기 때문에 프로그램이 필요로 하는 모든 내용입니다.
    npm run test
    

    8. 전체 CRUD(TDD 스타일)를 지원하도록 어플리케이션을 확장합니다.


    어셈블리 BookCreate 생성


    // resources/js/components/books/BookCreate.vue
    
    <template>
        <div class="container">
            <form @submit.prevent="saveBook">
                <div class="form-group">
                    <label>Title: </label>
                    <input type="text" class="form-control" placeholder="book title" v-model="form.title">
                </div>
                <div class="form-group">
                    <label>Year: </label>
                    <input type="text" class="form-control" placeholder="book year" v-model="form.year">
                </div>
                <div class="form-group">
                    <label>Author: </label>
                    <select class="form-control" v-model="form.author">
                    <option v-for="author in authors" :key="author">{{ author }}</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>Publisher: </label>
                    <select class="form-control" v-model="form.publisher">
                    <option v-for="publisher in publishers" :key="publisher">{{ publisher }}</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>Genre: </label>
                    <select class="form-control"  v-model="form.genre">
                    <option v-for="genre in genres" :key="genre">{{ genre }}</option>
                    </select>
                </div>
                <div class="form-group"><br/>
                    <button :disabled="!submittable" type="submit" class="btn btn-primary">Save</button>
                </div>
            </form>
        </div>
    </template>
    
    <script lang='ts'>
    import useBooks from '../../composables/books';
    import { reactive, computed } from 'vue';
    
    export default {
        setup() {
            const { errors, storeBook, authors, publishers, genres } = useBooks();
    
            const form = reactive({
                title: '',
                author: '',
                publisher: '',
                genre: '',
                year: null
            })
    
            const submittable = computed(() => {
                return form.title !== '' && form.author !== ''
                && form.publisher !== '' && form.genre !== '' && form.year !== null;
            });
    
    
            const saveBook = async () => {
                await storeBook({ ...form })
            }
    
            return {
                form,
                errors,
                saveBook,
                authors,
                publishers,
                genres,
                submittable
            }
        }
    }
    </script>
    
    이 새 구성 요소에 단추를 추가합니다.
    도서 색인에 추가하십시오.vue
    <template>
        <div class="container">
            <div class="row">
    +            <router-link :to="{ name: 'books.create' }" class="text-sm font-medium nav-link">
    +                <button type="button" class="btn btn-primary">Add book</button>
    +            </router-link>
            </div>
            <div class="card" style="width: 18rem; float: left; margin: 5px" v-for="book in books" :key="book.id">
            .....
    
    응용 프로그램에서 접근할 수 있도록 노선에 추가합니다.
    // resources/js/router/index.ts
    
    //...
        ,
        {
            path: '/books/create',
            name: 'books.create',
            component: BookCreate,
        },
    
    //...
    
    이제, 조합 가능한 책에create axios 호출을 추가합니다.vue 공유기를 사용하여 우리의 응용을 성공적인 색인 페이지로 바꿉니다.
    // resources/js/composables/books.ts
    
    import { ref } from 'vue';
    import axios from "axios";
    import { useRouter } from 'vue-router'; // import vue router
    
    export default function useBooks() {
        const books = ref([])
        const book = ref([])
        const errors = ref('') // stores errors coming from our call so that we can display 
        const router = useRouter(); // instatiante vue-router
    
        const authors = [ 'Terry A', 'Steven Price', 'John Smith', 'John Kennedy','Bryan Promise', 'Kyle David']; // fake authors options to select from when creating book
        const publishers = [ 'Publisher A', 'Publisher B', 'Publisher C', 'Publisher D']; // fake publishers options to select from when creating book
        const genres = ['Fiction', 'Non-Fiction', 'Business', 'Horror','Other']; // fake genres options to select from when creating book
    
        const getBooks = async () => {
            const response = await axios.get('/api/books');
            books.value = response.data.data;
        }
    
        const getBook = async (id: number) => {
            let response = await axios.get('/api/books/' + id)
            book.value = response.data.data;
        }
    
        const storeBook = async (data: object) => {
            errors.value = ''
            try {
                await axios.post('/api/books', data)
                await router.push({name: 'books.index'}) // redirect app to index page on success 
            } catch (e: any) {
                if (e.response.status === 422) {
                    errors.value = e.response.data.errors
                }
            }
        }
    
        return {
            authors,
            publishers,
            genres,
            errors,
            books,
            book,
            getBook,
            getBooks,
            storeBook
        }
    }
    
    
    구성 요소 테스트를 작성합니다. 모든 값이 채워졌는지 확인할 수 있는 제출 가능한 검사가 있습니다.
    그것은 하나의 계산 속성으로 실현되었다.
    
    import { ..., computed } from 'vue';
    ...
    const submittable = computed(() => {
                return form.title !== '' && form.author !== ''
                && form.publisher !== '' && form.genre !== '' && form.year !== null;
            });
    
    어쨌든, 이것은 최소한 두 개의 테스트 용례를 작성할 수 있는 충분한 특성이다. 하나는 반드시false이고, 다른 하나는 반대이다.
    jest 테스트 용례:
  • 모든 값이 작성된 경우 사용자가 제출할 수 있습니다.
  • it("allows submit when all values are set", async () => {
        // set up test component
        // fill out all the fields correctly
        //assert to be submittable
        expect(wrapper.vm.submittable).toBe(true);
      });
    
    
  • 모든 값이 입력되지 않으면 사용자가 제출할 수 없습니다.
  • it("disallows submit when all values are not set", async () => {
        // set up test component
        // fill out all the fields and leave out at least one
        //assert to NOT be submittable
        expect(wrapper.vm.submittable).toBe(false);
      });
    
    우리의 테스트
    // resources/js/tests/components/books/BookCreate.spec.ts
    
    import { mount, shallowMount, flushPromises } from "@vue/test-utils";
    import BookCreate from "../../../components/books/BookCreate.vue";
    import router from "../../../router";
    
    describe("BookIndex.vue", () => {
    
        beforeEach(() => {
        })
    
      it("allows submit when all values are set", async () => {
        const wrapper = shallowMount(BookCreate, {
          global: {
            plugins: [router],
          }
        } as any);
    
        await wrapper.find('#title').setValue('test title');
        await wrapper.find('#year').setValue(1994);
        await wrapper.find('#publisher').setValue('test p');
        await wrapper.find('#author').setValue('test a');
        await wrapper.find('#genre').setValue('test g');
    
        expect(wrapper.vm.submittable).toBe(true);
      });
    
      it("disallows submit when all values are set", async () => {
        const wrapper = shallowMount(BookCreate, {
          global: {
            plugins: [router],
          }
        } as any);
    
        expect(wrapper.vm.submittable).toBe(false);
      });
    });
    
    
    이 때 추가: 편집 및: 삭제 기능은 쉬울 것입니다.하지만 우리 이거 계속 끝내자.
           <h6 class="card-subtitle mb-2 text-muted">published by: {{ book.publisher}}</h6>
                <h6 class="card-subtitle mb-2 text-muted">genre: {{ book.genre}}</h6>
           </div>
    
     +      <div>
     +           <router-link id="editBtn" :to="{ name: 'books.edit' , params: { id: `${book.id}` }}">Edit</router-link>&nbsp;
     +           <a id="deleteBtn" @click="deleteBook(book)" href="#" role="button">Delete</a>&nbsp;
     +       </div>
    
    
           <div class="row">
               <div class="col-12 border">
    
    composable에 두 개의api 호출을 추가합니다
    // resources/js/composables/books.ts
    
    /** Edit book **/
    const updateBook = async (id: number) => {
        errors.value = ''
        try {
            await axios.put('/api/books/' + id, book.value)
            await router.push({name: 'books.index'})
        } catch (e: any) {
            if (e.response.status === 422) {
                errors.value = e.response.data.errors
            }
        }
    }
    
    /** Delete book **/
    
    const removeBook = async (id: number) => {
        await axios.delete('/api/books/' + id);
        await router.push({name: 'books.index'});
    }
    
    ...
    
    return {
        ...
        updateBook,
        removeBook
    }
    
    
    새 루트와 구성 요소를 알아보기 위해 vue 공유기를 업데이트합니다.
    // resources/js/router/index.ts
    
    ...
    import BookEdit from '../components/books/BookEdit.vue';
    
    ...
    ,
        {
            path: '/books/:id/edit',
            name: 'books.edit',
            component: BookEdit,
            props: true
        },
    ...
    
    새 BookEdit 어셈블리를 추가합니다.
    // resources/js/components/books/BookEdit.vue
    
    <template>
        <div class="container">
            <form @submit.prevent="editBook">
                <div class="form-group">
                    <label>Title: </label>
                    <input id="title" type="text" class="form-control" placeholder="book title" v-model="book.title">
                </div>
                <div class="form-group">
                    <label>Year: </label>
                    <input type="text" class="form-control" placeholder="book year" v-model="book.year" id="year">
                </div>
                <div class="form-group">
                    <label>Author: </label>
                    <select class="form-control" v-model="book.author" id="author">
                    <option v-for="author in authors" :key="author">{{ author }}</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>Publisher: </label>
                    <select class="form-control" v-model="book.publisher" id="publisher">
                    <option v-for="publisher in publishers" :key="publisher">{{ publisher }}</option>
                    </select>
                </div>
                <div class="form-group">
                    <label>Genre: </label>
                    <select class="form-control"  v-model="book.genre" id="genre">
                    <option v-for="genre in genres" :key="genre">{{ genre }}</option>
                    </select>
                </div>
                <div class="form-group"><br/>
                    <button type="submit" class="btn btn-primary">Save</button>
                </div>
            </form>
        </div>
    </template>
    
    <script lang='ts'>
    import useBooks from '../../composables/books';
    import { onMounted, computed } from 'vue';
    
    export default {
        props: {
           id: {
               required: true,
               type: String
           }
       },
        setup(props: any) {
            const { errors, authors, publishers, genres, book, getBook, updateBook } = useBooks();
    
            onMounted(() => getBook(props.id))
    
    
            const editBook = async () => {
                await updateBook(props.id);
            }
    
            return {
                book,
                errors,
                editBook,
                authors,
                publishers,
                genres
            }
        }
    }
    </script>
    
    
    이것들을 위해 예시 테스트를 추가합시다
    BookEdit vue 어셈블리 테스트
    // resources/js/tests/components/books/BookEdit.spec.ts
    
    
    import { mount, shallowMount, flushPromises } from "@vue/test-utils";
    import BookEdit from "../../../components/books/BookEdit.vue";
    import router from "../../../router";
    import axios from 'axios';
    
    jest.mock('axios');
    const mockedAxios = axios as jest.Mocked<typeof axios>;
    
    const testId = '3';
    const fakeBook = { "id": "3", "title": "book1", "subtitle": "hello1", "year": 1938, 'author': 'A', 'publisher': 'A', 'genre': ''}
    const fakeData = Promise.resolve({"data":{"data": fakeBook}});
    
    
    describe("BookEdit.vue", () => {
    
        beforeEach(() => {
        })
    
      it("correctly prepopulates form with correct existing data", async () => {
    
        mockedAxios.get.mockReturnValueOnce(fakeData);
    
        const wrapper = shallowMount(BookEdit, {
        propsData: {
            id: testId
        },
          global: {
            plugins: [router],
          }
        } as any);
    
        expect(axios.get).toBeCalledWith("/api/books/"+testId);
    
        await flushPromises();
    
        const titleInputField: HTMLInputElement = wrapper.find('#title').element as HTMLInputElement;
        const yearInputField: HTMLInputElement = wrapper.find('#year').element as HTMLInputElement;
    
        const prepopTitle = titleInputField.value;
        const prepopYear = yearInputField.value;
    
        expect(prepopTitle).toBe(fakeBook.title);
        expect(prepopYear).toBe(`${fakeBook.year}`);
      });
    
    });
    
    
    현재, delete는 자신의 구성 요소가 없기 때문에, 정확한 책 이름으로 delete를 눌렀을 때, delete 대화상자를 볼 수 있는지 테스트해 봅시다.
    우리들은 서적에 테스트 용례를 하나 추가할 것이다.규격.
    // resources/js/tests/components/books/BookShow.spec.ts
    
    ...
    it("shows user delete dialog on delete click.", async () => {
    
        mockedAxios.get.mockReturnValueOnce(fakeData);
        window.confirm = jest.fn(); // mock window.confirm implementation
    
        const wrapper = shallowMount(BookShow, {
        propsData: {
            id: testId
        },
          global: {
            plugins: [router],
          }
        } as any);
    
        await flushPromises();
    
    
        const button: HTMLElement = wrapper.find('#deleteBtn').element as HTMLElement;
        button.click();
    
        expect(window.confirm).toBeCalledWith(`delete  ${fakeBook.title}?`)
      });
    
    ...
    
    
    초보자로서, 나는 이것이 충분하다고 생각한다. jest가jest 프로필에 다음 내용을 추가해서 테스트 범위율을 평가할 수 있다.
    ...
    collectCoverage: true,
        "collectCoverageFrom": [
            "resources/js/**/*.{js,jsx}",
            "resources/js/**/*.{ts,tsx}",
            "resources/js/**/*.vue",
            "!resources/js/tests/**/*.*",
            "!**/node_modules/**",
            "!**/vendor/**"
          ],
          ...
    
    그리고 뛰다
    npm run test 
    

    마지막 작업 예:


    https://github.com/LufunoMaphwanya/laraVue3-typescript-example

    참조 자료:


    https://laraveldaily.com/laravel-8-vue-3-crud-composition-api/
    https://next.vue-test-utils.vuejs.org/guide/

    좋은 웹페이지 즐겨찾기