













Preview text:
LO1 Investigate the impact of SOLID development principles on the OOP paradigm.
P1 Investigate the characteristics of the object- orientated paradigm, including class
relationships and SOLID principles.
• Trình bày về lập trình hướng đối tượng? Tại sao lập trình hướng đối tượng được áp dụng
rộng rãi trong các phần mềm lớn, phức tạp ?
Lập trình hướng đối tượng (OOP) là một kỹ thuật lập trình cho phép lập trình viên tạo ra các đối
tượng trong code trừu tượng hóa các đối tượng.
• Trình bày về lớp và đối tượng.
Một đối tượng bao gồm 2 thông tin: thuộc tính và phương thức.
Thuộc tính chính là những thông tin, đặc điểm của đối tượng. Ví dụ: con người có các đặc
tính như mắt, mũi, tay, chân…
Phương thức là những thao tác, hành động mà đối tượng đó có thể thực hiện. Ví dụ: một
người sẽ có thể thực hiện hành động nói, đi, ăn, uống, . . .
Một lớp là một kiểu dữ liệu bao gồm các thuộc tính và các phương thức được định nghĩa từ trước.
Đây là sự trừu tượng hóa của đối tượng. Khác với kiểu dữ liệu thông thường, một lớp là một đơn
vị (trừu tượng) bao gồm sự kết hợp giữa các phương thức và các thuộc tính. Hiểu nôm na hơn là
các đối tượng có các đặc tính tương tự nhau được gom lại thành một lớp đối tượng.
• Trình bày vè các tính các tính chất: tính đóng gói, tính kế thừa, tính đa hình và trừu tượng.
Tính đóng gói (Encapsulation)
Các dữ liệu và phương thức có liên quan với nhau được đóng gói thành các lớp để tiện cho
việc quản lý và sử dụng. Tức là mỗi lớp được xây dựng để thực hiện một nhóm chức năng
đặc trưng của riêng lớp đó.
Ngoài ra, đóng gói còn để che giấu một số thông tin và chi tiết cài đặt nội bộ để bên ngoài không thể nhìn thấy.
Nói chung trạng thái đối tượng không hợp lệ thường do: chưa được kiểm tra tính hợp lệ, các bước
thực hiện không đúng trình tự hoặc bị bỏ qua nên trong OOP có một quy tắc quan trọng cần nhớ
đó là phải luôn khai báo các trạng thái bên trong của đối tượng là private và chỉ cho truy cập qua
các public/protected method/property.
Tính kế thừa (Inheritance)
Nó cho phép xây dựng một lớp mới dựa trên các định nghĩa của lớp đã có. Có nghĩa là lớp
cha có thể chia sẽ dữ liệu và phương thức cho các lớp con. Các lớp con khỏi phải định
nghĩa lại, ngoài ra có thể mở rộng các thành phần kế thừa và bổ sung thêm các thành phần
mới. Tái sử dụng mã nguồn 1 cách tối ưu, tận dụng được mã nguồn. Một số loại kế loại kế
thừa thường gặp: đơn kế thừa, đa kế thừa, kế thừa đa cấp, kế thừa thứ bậc.
Khi bắt đầu xây dựng ứng dụng chúng ta sẽ bắt đầu thiết kế định nghĩa các lớp trước. Thông
thường một số lớp có quan hệ với những lớp khác, chúng có những đặc tính giống nhau.
Tính đa hình (Polymorphism)
Tính đa hình là một hành động có thể được thực hiện bằng nhiều cách khác nhau. Đây lại
là một tính chất có thể nói là chứa đựng hầu hết sức mạnh của lập trình hướng đối tượng.
Hiểu một cách đơn giản hơn: Đa hình là khái niệm mà hai hoặc nhiều lớp có những phương
thức giống nhau nhưng có thể thực thi theo những cách thức khác nhau.
Tính trừu tượng (Abstraction)
Trừu tượng có nghĩ là tổng quát hóa một cái gì đó lên, không cần chú ý chi tiết bên trong.
Nó không màng đến chi tiết bên trong là gì và người ta vẫn hiểu nó mỗi khi nghe về nó.
Ví dụ: Bạn chạy xe tay ga thì có hành động là tăng ga để tăng tốc, thì chức năng tăng ga là
đại diện cho trừu tượng (abstraction). Người dùng chỉ cần biết là tăng ga thì xe tăng tốc,
không cần biết bên trong nó làm thế nào.
Ở đây trong lập trình OOP, tính trừu tượng nghĩa là chọn ra các thuộc tính, phương thức
của đối tượng cần cho việc giải quyết bài toán đang lập trình. Vì một đối tượng có rất nhiều
thuộc tính phương thức, nhưng với bài toán cụ thể không nhất thiết phải chọn tất cả.
• Nêu mối quan hệ giữa các lớp: kế thừa, liên kết, kết tập...
• Trình bày về nguyên lý Solid: nêu ra các nguyên lý.
SOLID is a set of software design principles designed to create flexible, maintainable, and
extensible software systems. This stands for the following rules:
Single Responsibility Principle (SRP) - Single Responsibility Principle:
Each class or module should have only one responsibility and one reason to change. In
other words, each software component should have only one specific task.
Example: Consider a "Customer" class in a sales application. This layer should only be
responsible for managing customer information such as name, address, phone number, and
should not be responsible for payment processing.
Open-Closed Principle (OCP) - Open-Closed Principle:
Software components should be designed to be extensible, but closed to change. This
means they should allow functionality to be extended by adding new code, without modifying existing code.
Example: Consider an application that calculates the area of geometries. Instead of using a
single method in the "Shape" class to calculate the area for every geometry type (square,
rectangle, circle), we can create separate classes for each shape type. study and each class
has its own method of calculating the area.
Liskov Substitution Principle (LSP) - Liskov Substitution Principle:
Objects of a subclass should be able to replace objects of the superclass without changing
the correctness of the program. This ensures that inheritance is used correctly and does not
cause conflicts or errors in the system.
Example: Consider a system that is using superclass "Animal" and subclass "Bird".
According to the Liskov Substitution Principle (LSP), every method of the superclass
"Animal" should be replaceable with the object of the subclass "Bird" without changing
the correctness of the program. This means that the methods of the superclass should work
correctly with the object of the child class.
Interface Segregation Principle (ISP) - Interface Segregation Principle:
Instead of having a large interface containing many methods, it is advisable to create
smaller interfaces, containing only related methods. This principle encourages interface
separation to avoid implementing unnecessary methods.
Example: Consider a "Printer" interface with "print", "scan", and "fax" methods. If there is
a class "LaserPrinter" that only needs to implement the "print" method, while the "scan"
and "fax" methods are not needed, we should separate the "Printer" interface into smaller
interfaces like "Printable" , "Scannable", and "Faxable" so that the classes only implement the necessary interfaces.
Dependency Inversion Principle (DIP) - Dependency Inversion Principle:
High-level modules should not directly depend on low-level modules. Instead, both should
depend on an abstraction. This principle encourages the use of interfaces or intermediate
classes to reduce binding between modules and create flexibility in changes.
Example: Consider an application that has a "Database" class to retrieve data from a
database. Instead of class "User" which depends directly on class "Database", we can use
an interface "IDatabase" and class "Database" implement this interface. The "User" class
will depend on the "IDatabase" interface, not the "Database" class directly.
P2 Explain how clean coding techniques can impact on the use of data structures and
operations when writing algorithms.
• Trình bày về clean code là gì? Lợi ích áp dụng clean code.
Clean code nếu dịch ra thì có nghĩa là “mã nguồn sạch”, nhưng hiểu một cách đơn giản thì clean
code bao gồm: cách tổ chức mã nguồn, cách triển khai mã nguồn sao cho khoa học, dễ hiểu và
đem lại hiệu năng cao cho chương trình.
Việc áp dụng clean code không khó, nhưng áp dụng làm sao cho đúng và chuẩn thì lại là một câu
chuyện khác. Chính vì vậy mà việc nắm được, hiểu và biết cách áp dụng clean code sẽ khiến cho
mã nguồn tốt hơn rất nhiều.
Một số nguyên tắc chính của clean code bao gồm:
1) Đơn giản và ngắn gọn: Mã nguồn cần được viết đơn giản, tránh sự phức tạp không cần
thiết. Điều này giúp dễ dàng hiểu và bảo trì mã nguồn.
2) Đặt tên rõ ràng: Tên biến, tên hàm và tên lớp cần phản ánh mục đích và chức năng của
chúng. Việc đặt tên mô tả ngắn gọn và chính xác sẽ giúp người đọc hiểu mã nguồn nhanh chóng.
3) Ngắn gọn và thể hiện ý nghĩa: Hàm và phương thức cần được viết ngắn gọn, chỉ thực hiện
một nhiệm vụ duy nhất và có tên phản ánh chính xác chức năng của chúng.
4) Tránh lặp lại: Mã nguồn cần tránh lặp lại thông qua việc sử dụng các hàm, lớp hoặc thư
viện có sẵn. Điều này giúp giảm sự rườm rà và dễ dàng bảo trì.
5) Đảm bảo đọc hiểu: Clean code cần được viết sao cho dễ đọc và hiểu. Bằng cách sử dụng
định dạng, bình luận và phân cấp rõ ràng, người đọc có thể nắm bắt mã nguồn một cách nhanh chóng.
Lợi ích của việc áp dụng clean code trong phát triển phần mềm là:
1) Dễ bảo trì: Mã nguồn được viết theo clean code sẽ dễ dàng bảo trì hơn. Người phát triển
mới hoặc thành viên trong nhóm phát triển khác có thể nhanh chóng hiểu và chỉnh sửa mã mà không gặp khó khăn.
2) Tăng tính linh hoạt: Clean code giúp tách biệt các thành phần riêng biệt và giảm sự phụ
thuộc giữa chúng. Điều này tạo điều kiện thuận lợi để thay đổi hoặc mở rộng chức năng
mà không làm ảnh hưởng đến các phần khác của mã nguồn.
3) Tăng độ tin cậy: Clean code giúp giảm lỗi và bug trong quá trình phát triển. Mã nguồn rõ
ràng và dễ hiểu giúp phát hiện lỗi một cách nhanh chóng và dễ dàng sửa chữa.
4) Tiết kiệm thời gian: Clean code giúp tiết kiệm thời gian trong quá trình phát triển và bảo
trì phần mềm. Mã nguồn dễ đọc và dễ hiểu giúp giảm thời gian tìm hiểu mã nguồn và tăng hiệu suất làm việc.
5) Tăng khả năng hợp tác: Clean code làm cho mã nguồn trở nên dễ đọc và dễ hiểu đối với
các thành viên trong nhóm phát triển. Điều này tạo điều kiện thuận lợi cho việc làm việc
nhóm và tăng khả năng hợp tác trong quá trình phát triển phần mềm.
• Đưa ra các cách để áp dụng clean code. (không cần lấy ví dụ chi tiết).
1) Đặt tên rõ ràng: Chọn tên biến, tên hàm và tên lớp có ý nghĩa và phản ánh chính xác mục
đích và chức năng của chúng.
2) Hàm chỉ thực hiện một nhiệm vụ: Hãy đảm bảo rằng các hàm và phương thức chỉ thực hiện
một nhiệm vụ duy nhất và có kích thước nhỏ.
3) Tránh lặp lại: Sử dụng tái sử dụng mã nguồn thông qua việc tách các phần logic chung vào
các hàm, lớp hoặc thư viện riêng biệt.
4) Viết mã ngắn gọn và đơn giản: Tránh sự phức tạp không cần thiết và viết mã nguồn ngắn
gọn, dễ đọc và dễ hiểu.
5) Định dạng mã nguồn: Sử dụng các quy tắc định dạng như thụt lề, khoảng trắng và viết hoa
chữ cái đầu dòng để tạo ra mã nguồn có cấu trúc rõ ràng.
6) Bình luận mã nguồn: Sử dụng bình luận để giải thích ý nghĩa và logic của mã nguồn. Bình
luận cần được viết một cách rõ ràng và không gây hiểu nhầm.
7) Kiểm thử và sửa lỗi: Thực hiện kiểm thử đầy đủ để đảm bảo mã nguồn hoạt động đúng và
không có lỗi. Sửa lỗi ngay khi phát hiện để duy trì mã nguồn trong tình trạng sạch.
8) Liên tục cải thiện: Luôn cải thiện và làm cho mã nguồn trở nên tốt hơn. Duy trì sự liên tục
trong việc tái cấu trúc và tối ưu hóa mã nguồn.
9) Đọc mã nguồn của người khác: Đọc và hiểu mã nguồn của người khác giúp bạn học hỏi và
áp dụng các phong cách viết code tốt.
10)Đánh giá mã nguồn: Đánh giá mã nguồn của mình và nhận phản hồi từ người khác để cải
thiện chất lượng và thực hiện clean code một cách liên tục.
• Clean code còn áp dụng trong việc lựa chọn các cấu trúc dữ liệu phù hợp. Khi xây dựng
mộtứng dụng phải thực hiện việc tìm kiếm thường xuyên, tại sao nên sử dụng BinarySearch
hơn Linear Search=> Trình bày ý tưởng, so sánh ưu điểm của Binary Search so với Linear
Search Nên sử dụng BinarySearch hơn Linear Search vì:
Hiệu suất tốt hơn: Binary Search thường có hiệu suất tìm kiếm tốt hơn đáng kể so với
Linear Search. Với Linear Search, thời gian tìm kiếm tăng theo cấp số tuyến tính (O(n)),
trong khi Binary Search có độ phức tạp thời gian là O(log n). Điều này đặc biệt quan trọng
khi số lượng phần tử lớn, vì Binary Search chỉ cần ít bước để tìm kiếm trong một dãy đã sắp xếp.
Dùng cho dãy đã sắp xếp: Binary Search chỉ áp dụng cho các dãy đã được sắp xếp, nhưng
khi đã có dãy sắp xếp, nó có thể tận dụng được tính chất này để thực hiện tìm kiếm nhanh
chóng hơn. Trong khi Linear Search không yêu cầu dãy đã sắp xếp, nhưng nó phải tìm kiếm
từ đầu đến cuối dãy một cách tuần tự, dẫn đến hiệu suất kém hơn.
Tiết kiệm tài nguyên: Sử dụng Binary Search có thể giúp tiết kiệm tài nguyên bởi vì số lần
so sánh cần thiết ít hơn so với Linear Search. Với Binary Search, việc tìm kiếm được thực
hiện trên một phần của dãy, giảm thiểu số lượng phép so sánh cần thiết để tìm kiếm phần tử.
Đơn giản và dễ hiểu: Binary Search có một thuật toán đơn giản và dễ hiểu. Ý tưởng của nó
là phân chia dãy thành các phần bằng nhau và so sánh giá trị tìm kiếm với phần tử ở giữa.
Điều này làm cho nó dễ dàng áp dụng và kiểm tra tính đúng đắn. Ý tưởng cơ bản:
o Binary Search được áp dụng cho các dãy đã được sắp xếp (thường là dãy tăng dần).
o Ý tưởng chính của Binary Search là tìm kiếm thông qua việc chia dãy thành các phần bằng
nhau và so sánh giá trị tìm kiếm với phần tử ở giữa.
o Nếu giá trị tìm kiếm bằng giá trị ở giữa, phần tử đã được tìm thấy. o Nếu giá trị tìm kiếm
nhỏ hơn giá trị ở giữa, tiếp tục tìm kiếm trong nửa đầu của dãy. o Nếu giá trị tìm kiếm lớn
hơn giá trị ở giữa, tiếp tục tìm kiếm trong nửa sau của dãy.
o Quá trình tìm kiếm tiếp tục trên nửa dãy đã chọn, cho đến khi tìm thấy phần tử hoặc xác
định không có phần tử cần tìm trong dãy.
So sánh ưu điểm của Binary Search so với Linear Search:
Hiệu suất: Binary Search thường có hiệu suất tìm kiếm tốt hơn so với Linear Search. Trong
Linear Search, thời gian tìm kiếm tăng theo cấp số tuyến tính với kích thước dữ liệu (O(n)),
trong khi Binary Search có độ phức tạp thời gian là O(log n). Điều này làm cho Binary
Search nhanh hơn đáng kể khi kích thước dữ liệu lớn.
Số lần so sánh: Số lần so sánh cần thiết để tìm kiếm phần tử giảm một cách đáng kể với
Binary Search. Trong Linear Search, số lần so sánh tăng theo tỉ lệ tuyến tính với kích
thước dữ liệu. Trong khi đó, Binary Search giảm số lần so sánh một cách đáng kể. Mỗi lần
tìm kiếm, kích thước dãy cần xem xét giảm đi một nửa.
Độ phức tạp: Độ phức tạp thời gian của Linear Search là O(n), trong khi Binary Search có
độ phức tạp thời gian là O(log n). Điều này đồng nghĩa với việc Binary Search có thể xử
lý dữ liệu lớn hơn một cách hiệu quả hơn.
M1 Analyse, with examples, each of the creational, structural and behavioural design pattern types.
• Giải thích thuật ngữ “Design Pattern” ?
Design Patterns are a general solution to common problems in software design. It is an inherited
and proven approach to solving programming problems.
A design pattern is not a specific code or a library that you can copy and paste into your program.
Instead, it provides an abstract description of how to solve a particular problem. Design patterns
help create flexible, maintainable, and extensible designs in software development.
There are many different design patterns, each of which focuses on solving a specific problem in
software design. Some of the popular design patterns include Singleton Pattern, Strategy Pattern,
Observer Pattern, Factory Pattern, Composite Pattern and many more.
The purpose of design patterns is to provide a standardized approach to software design, which
increases flexibility, code reuse, reduces errors, and increases performance during software development.
• Kể tên các nhóm design pattern và các mẫu thiết kế trong mỗi nhóm.
Design Patterns are classified into three main groups: Creational Patterns, Structural Patterns, and
Behavioral Patterns. Here is a list of Design Patterns in each group: Creational Patterns:
o Singleton Pattern (Singleton Pattern) o Factory Method Pattern (Factory Method Pattern)
o Abstract Factory Pattern (Abstract Factory Pattern) o Builder Pattern o Prototype Pattern
Structural Patterns (Structural Patterns):
o Adapter Pattern o Bridge Pattern o Composite Pattern o Decorator Pattern o Facade
Pattern o Flyweight Pattern (Flyweight Pattern) o Proxy Pattern
Behavioral Patterns (Behavior Patterns):
o Observer Pattern o Strategy Pattern o Command Pattern o Iterator Pattern o Template
Method Pattern o State Pattern o Chain of Responsibility Pattern o Visitor Pattern o
Interpreter Pattern o Mediator Pattern o Memento Pattern
• Trong mỗi nhóm design pattern, hãy lấy một ví dụ cụ thể bao gồm class diagram và codetriển khai. Nhóm Creational Nhóm Structural Nhóm Behavioral
LO2 Design a large dataset processing application using SOLID principles and clean coding techniques
P3 Design a large data set processing application, utilising SOLID principles, clean coding
techniques and a design pattern.
P4 Design a suitable testing regime for the application, including provision for automated testing.
• Trình bày về kiểm thử là gì? Tại sao cần kiểm thử?
Testing is the process of evaluating a software, system, or application to ensure that it works as
intended and meets set requirements. Testing helps to identify bugs, problems, and
nonconformities in the software, while ensuring its stability, reliability, and efficiency.
The benefits of testing include:
Defect Detection: Testing helps to detect and document bugs, errors or unexpected behavior
in the software. By finding and fixing these errors, the stability and reliability of the system can be improved.
Quality Assurance: Testing helps to ensure the quality of the software. By testing every
aspect and function of the system, we can ensure that it works as expected and meets the requirements.
Ensure consistency: Testing helps to ensure consistency between software components and
keeps it working as a unified system.
Risk Reduction: Testing helps to reduce the risks associated with software implementation.
By identifying and fixing bugs before the software is put into use, potential problems can
be avoided and the risk of serious consequences is reduced.
Increased reliability: Testing plays an important role in increasing the reliability of the
software. By identifying and fixing problems, we can ensure that the software works stably
and reliably in real-life situations.
Save time and resources: Although testing requires an investment of time and resources, it
can save time and resources in the future. By detecting and fixing bugs early, we avoid
having to deal with more complex problems and save time during development and deployment.
• Trình bày về kiểm thử đơn vị, kiểm thử tích hợp, kiểm thử hệ thống, kiểm thử người dùng? Unit test:
o Unit tests test the smallest pieces of software such as functions, methods, classes, and
modules to ensure that they work properly and meet requirements.
o The purpose of unit testing is to isolate components and verify that they behave as intended
and do not affect other components. Integration test:
o Integration testing is the process of verifying the functionality of unit-tested components
when combined and linked together.
o The purpose of integration testing is to identify problems related to interactions between
components and to ensure that the components behave correctly and consistently. System test:
o System testing tests the entire embedded software system to ensure that it meets user needs and functionality.
o The purpose of system testing is to check the correctness, reliability, and efficiency of the
system, verify the interactions between components, and identify system errors. User Acceptance Testing:
o User testing tests the software from the end user's perspective to ensure that it meets the
requirements and is ready to use.
o The goal of user testing is to ensure that software meets technical and business
requirements, interacts meaningfully with end users, and provides a good user experience.
• Đưa ra một ví dụ cụ thể về việc áp dụng JUNIT trong kiểm thử.
JUnit is a simple framework for creating automated unit tests, and running reproducible tests. It
is just part of the xUnit family of architectures for creating unit tests. JUnit is the de facto
standard for unit testing in Java.
M2 Refine the design to include multiple design patterns.
• Thay đổi thiết kế class diagram ở P3, sử dụng ít nhất 2 design pattern trong đó LO1-LO2:
D1 Evaluate the impact of SOLID development principles on object- orientated application development.
• Đánh gía tác động của nguyên lý Solid trong phát triển ứng dụng hướng đối tượng. Cần
nêu ví dụ giải thích.
Single Responsibility Principle (SRP – Single Responsibility):
o Impact: Each class should only have one responsibility of hers.
o Example: Let's say you have a class "User" in your user management system. Under SRP
principles, this class should only have responsibilities related to managing user information
such as name, email, password, and user-related methods. If this class also handles
independent functionality such as sending registration confirmation emails, this violates
the SRP principle. In this case, you should split the email sending functionality into separate
classes so that each class has only one responsibility for her.
Open Closed Principle (OCP – Open, Closed):
o Impact: Extending new functionality requires opening the source code, but modifying
existing source code requires closing the source code.
o Example: Suppose you have a class "Shape" with an area method. The OCP principle states
that when you add a new shape you should extend this layer and create a sublayer for each
new shape instead of modifying the "shape" layer. For example, if you want to add a square,
you should create a class "Square" that inherits from the class "Shape" and implement
another method to calculate the area of the square.
Liskov Substitution Principle (LSP – Liskov Substitution):
o Impact: Subclasses should be able to replace parent classes without changing program
correctness. Example: Suppose you have a class "Animal" and subclasses "Cat", "Dog",
"Bird", etc. LSP principles dictate that these subclasses should be used to replace the
Animal class without changing the functionality of the program.
o For example, if the 'Animal' class has a 'makeSound' method to make a sound, all subclasses
should also implement this method unchanged.
Interface Separation Principle (ISP – Interface Separation):
o Impact: It is recommended to split large interfaces into smaller interfaces to avoid
unnecessary implementation of methods.
o Example: Let's say you have an interface 'Printable' with a 'print' method. However, not all
classes need to implement this method. ISP policy requires that the Printable interface be
split into smaller interfaces such as PrintableDocument and PrintableImage. Classes only
need to implement the relevant interfaces, reducing unnecessary dependencies and
avoiding independent method implementations.
Dependency Inversion Principle (DIP):
o Impact: Higher level modules should not depend directly on lower level modules, but both
should depend on abstractions.
o Example: Let's say he has two classes in his application, 'Database' and 'User'. According
to the DIP principle, instead of the "User" class calling the methods of the "Database" class
directly, it should create a "DatabaseInterface" interface, and the "User" class depends on
this interface. The Database class implements this interface. This separates user classes
from database classes, reduces dependencies, and makes applications more flexible.
LO3 Build a data processing application based on a developed design P5
Build a large dataset processing application based on the design produced.
• Triển khai phần thiết kế ở P3.
M3 Assess the effectiveness of using SOLID principles, clean coding techniques and
programming patterns on the application developed.
• Giải thích, đánh giá việc áp dụng nguyên lý Solid, clean code và design pattern ở P5.
LO4 Perform automatic testing on a data processing application
P6 Examine the different methods of implementing automatic testing as designed in the test plan.
• Trình bày về kiểm thử theo TDD và BDD.
P7 Implement automatic testing of the developed application.
• Triển khai kiểm thử Junit với ứng dụng đã thiết kế.
M4 Discuss the differences between developer- produced and vendor- provided automatic
testing tools for applications and software systems.
• Thảo luận về sự khác biệt giữa các công cụ kiểm tra tự động do nhà phát triển sản xuất
vànhà cung cấp cung cấp cho các ứng dụng và hệ thống phần mềm LO3-LO4:
D2 Analyse the benefits and drawbacks of different forms of automatic testing of
applications and software systems, with examples from the developed application.
• Phân tích lợi ích và hạn chế của các hình thức kiểm tra tự động khác nhau của các ứng
dụng và hệ thống phần mềm, với các ví dụ từ ứng dụng đã phát triển ở P5