[英]Test class methods using Pytest
我现在正在上一门编程课程,CSE 111 programming with functions,我的最后一个任务是找到一个有趣的代码并编写一些测试函数,以测试代码是否正确工作。 这是代码:
#Imported modules
import sys
import wave, contextlib, math, time
import speech_recognition as sr
from moviepy.editor import AudioFileClip
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QThread, pyqtSignal
class Ui_MainWindow(object):
"""Main window GUI."""
def __init__(self):
"""Initialisation function."""
self.mp4_file_name = ""
self.output_file = ""
self.audio_file = "speech.wav"
def setupUi(self, MainWindow):
"""Define visual components and positions."""
# Main window
MainWindow.setObjectName("MainWindow")
MainWindow.resize(653, 836)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 20, 161, 41))
# Selected video file label
font = QtGui.QFont()
font.setPointSize(14)
self.label.setFont(font)
self.label.setObjectName("label")
self.selected_video_label = QtWidgets.QLabel(self.centralwidget)
self.selected_video_label.setGeometry(QtCore.QRect(230, 20, 371, 41))
font = QtGui.QFont()
font.setPointSize(8)
self.selected_video_label.setFont(font)
self.selected_video_label.setFrameShape(QtWidgets.QFrame.Box)
self.selected_video_label.setText("")
self.selected_video_label.setObjectName("selected_video_label")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(50, 90, 161, 41))
# Transcribed text box
font = QtGui.QFont()
font.setPointSize(14)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.transcribed_text = QtWidgets.QTextBrowser(self.centralwidget)
self.transcribed_text.setGeometry(QtCore.QRect(230, 320, 381, 431))
self.transcribed_text.setObjectName("transcribed_text")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(230, 280, 161, 41))
font = QtGui.QFont()
font.setPointSize(14)
self.label_5.setFont(font)
self.label_5.setObjectName("label_5")
self.transcribe_button = QtWidgets.QPushButton(self.centralwidget)
self.transcribe_button.setEnabled(False)
self.transcribe_button.setGeometry(QtCore.QRect(230, 150, 221, 81))
# Transcribe button
font = QtGui.QFont()
font.setPointSize(14)
self.transcribe_button.setFont(font)
self.transcribe_button.setObjectName("transcribe_button")
self.transcribe_button.clicked.connect(self.process_and_transcribe_audio)
# progeress bar
self.progress_bar = QtWidgets.QProgressBar(self.centralwidget)
self.progress_bar.setGeometry(QtCore.QRect(230, 250, 381, 23))
self.progress_bar.setProperty("value", 0)
self.progress_bar.setObjectName("progress_bar")
self.message_label = QtWidgets.QLabel(self.centralwidget)
self.message_label.setGeometry(QtCore.QRect(0, 760, 651, 21))
# Message label (for errors and warnings)
font = QtGui.QFont()
font.setPointSize(8)
self.message_label.setFont(font)
self.message_label.setFrameShape(QtWidgets.QFrame.Box)
self.message_label.setText("")
self.message_label.setObjectName("message_label")
self.output_file_name = QtWidgets.QPlainTextEdit(self.centralwidget)
self.output_file_name.setGeometry(QtCore.QRect(230, 90, 371, 41))
# Output file name
font = QtGui.QFont()
font.setPointSize(14)
self.output_file_name.setFont(font)
self.output_file_name.setObjectName("output_file_name")
# Menubar options
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 653, 21))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
self.menuAbout = QtWidgets.QMenu(self.menubar)
self.menuAbout.setObjectName("menuAbout")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.actionOpen_mp4_video_recording = QtWidgets.QAction(MainWindow)
self.actionOpen_mp4_video_recording.setObjectName("actionOpen_mp4_video_recording")
self.actionOpen_mp4_video_recording.triggered.connect(self.open_audio_file)
self.actionAbout_vid2text = QtWidgets.QAction(MainWindow)
self.actionAbout_vid2text.setObjectName("actionAbout_vid2text")
self.actionAbout_vid2text.triggered.connect(self.show_about)
self.actionNew = QtWidgets.QAction(MainWindow)
self.actionNew.setObjectName("actionNew")
self.actionNew.triggered.connect(self.new_project)
self.menuFile.addAction(self.actionOpen_mp4_video_recording)
self.menuFile.addAction(self.actionNew)
self.menuAbout.addAction(self.actionAbout_vid2text)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuAbout.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
"""Translate UI method."""
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "Selected video file:"))
self.label_3.setText(_translate("MainWindow", "Output file name:"))
self.label_5.setText(_translate("MainWindow", "Transcribed text:"))
self.transcribe_button.setText(_translate("MainWindow", "Transcribe"))
self.output_file_name.setPlaceholderText(_translate("MainWindow", "interview1.txt"))
self.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuAbout.setTitle(_translate("MainWindow", "About"))
self.actionOpen_mp4_video_recording.setText(_translate("MainWindow", "Open mp4 video recording"))
self.actionAbout_vid2text.setText(_translate("MainWindow", "About video to speech"))
self.actionNew.setText(_translate("MainWindow", "New"))
def open_audio_file(self):
"""Open the audio (*.mp4) file."""
file_name = QFileDialog.getOpenFileName()
if file_name[0][-3:] == "mp4":
self.transcribe_button.setEnabled(True)
self.mp4_file_name = file_name[0]
self.selected_video_label.setText(file_name[0])
else:
self.message_label.setText("Please select an *.mp4 file")
def convert_mp4_to_wav(self):
"""Convert the mp4 video file into an audio file."""
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_thread = convertVideoToAudioThread(self.mp4_file_name, self.audio_file)
self.convert_thread.finished.connect(self.finished_converting)
self.convert_thread.start()
def get_audio_duration(self, audio_file):
"""Determine the length of the audio file."""
with contextlib.closing(wave.open(audio_file,'r')) as f:
frames = f.getnframes()
rate = f.getframerate()
duration = frames / float(rate)
return duration
def transcribe_audio(self, audio_file):
"""Transcribe the audio file."""
total_duration = self.get_audio_duration(audio_file) / 10
total_duration = math.ceil(total_duration)
self.td = total_duration
if len(self.output_file_name.toPlainText()) > 0:
self.output_file = self.output_file_name.toPlainText()
else:
self.output_file = "my_speech_file.txt"
# Use thread to process in the background and avoid freezing the GUI
self.thread = transcriptionThread(total_duration, audio_file, self.output_file)
self.thread.finished.connect(self.finished_transcribing)
self.thread.change_value.connect(self.set_progress_value)
self.thread.start()
def finished_converting(self):
"""Reset message text when conversion is finished."""
self.message_label.setText("Transcribing file...")
self.transcribe_audio(self.audio_file)
def finished_transcribing(self):
"""This run when transcription finished to tidy up UI."""
self.progress_bar.setValue(100)
self.transcribe_button.setEnabled(True)
self.message_label.setText("")
self.update_text_output()
def set_progress_value(self, val):
"""Update progress bar value."""
increment = int(math.floor(100*(float(val)/self.td)))
self.progress_bar.setValue(increment)
def process_and_transcribe_audio(self):
"""Process the audio into a textual transcription."""
self.transcribe_button.setEnabled(False)
self.message_label.setText("Converting mp4 to audio (*.wav)...")
self.convert_mp4_to_wav()
def update_text_output(self):
"""Update the text box with the transcribed file."""
f = open(self.output_file, "r")
self.transcribed_text.setText(f.read())
f.close()
def new_project(self):
"""Clear existing fields of data."""
self.message_label.setText("")
self.transcribed_text.setText("")
self.selected_video_label.setText("")
self.output_file_name.document().setPlainText("")
self.progress_bar.setValue(0)
def show_about(self):
"""Show about message box."""
msg = QMessageBox()
msg.setWindowTitle("About video to Speech")
msg.setText("Nelson Petro - CSE 111")
msg.setIcon(QMessageBox.Information)
msg.exec_()
class convertVideoToAudioThread(QThread):
"""Thread to convert mp4 video file to wav file."""
def __init__(self, mp4_file_name, audio_file):
"""Initialization function."""
QThread.__init__(self)
self.mp4_file_name = mp4_file_name
self.audio_file = audio_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run video conversion task."""
audio_clip = AudioFileClip(self.mp4_file_name)
audio_clip.write_audiofile(self.audio_file)
class transcriptionThread(QThread):
"""Thread to transcribe file from audio to text."""
change_value = pyqtSignal(int)
def __init__(self, total_duration, audio_file, output_file):
"""Initialization function."""
QThread.__init__(self)
self.total_duration = total_duration
self.audio_file = audio_file
self.output_file = output_file
def __del__(self):
"""Destructor."""
self.wait()
def run(self):
"""Run transcription, audio to text."""
r = sr.Recognizer()
for i in range(0, self.total_duration):
try:
with sr.AudioFile(self.audio_file) as source:
audio = r.record(source, offset=i*10, duration=10)
f = open(self.output_file, "a")
f.write(r.recognize_google(audio))
f.write(" ")
self.change_value.emit(i)
except:
print("Unknown word detected...")
continue
f.close()
def main():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
并且我已经编写了这两个测试函数。 第一个测试MainWindow
构造函数的默认值,第二个测试open_audio_file()
function 的正确功能(使用上一个问题中提供的说明)。 我对 OOP 不是很熟悉,所以如果你能帮助我让第二个 function 正常工作,我将不胜感激。
这是两个测试函数:
def test_MainWindowConstructor():
ui = Ui_MainWindow()
assert ui.mp4_file_name == ""
assert ui.output_file == ""
assert ui.audio_file == "speech.wav"
def test_that_providing_a_raw_file_is_rejected():
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
ui.actionOpen_mp4_video_recording.triggered
QFileDialog.getOpenFileName = "video.raw"
ui.open_audio_file
assert ui.message_label.text == "Please select an *.mp4 file"
非常感谢你。
你写了:
ui.actionOpen_mp4_video_recording.triggered
...
ui.open_audio_file
正如所写,这没有意义。 您可能想要调用 function,而不是评估表达式的副作用然后丢弃它。 fn
和fn()
之间有很大的区别——后者实际上调用了 function。
您正在成功验证triggered
的属性是否存在,这对单元测试很有帮助。 要验证,请尝试一些拼写错误,例如“fliggered”,并注意引发的 AttributeError。
同样,您正在验证open_audio_file
属性是否存在。 这是一个更简单的情况,因此单元测试这样做可能用处不大。 最好拨打 function,并验证其是否按预期运行。
尝试以这种方式运行测试:
$ pytest --cov --cov-report=term-missing
这将帮助您注意到您应该测试两种音频文件:带和不带“.mp4”后缀。
请注意,目标代码有时会提到 function 而没有调用它,也没有添加()
。 那里发生了什么事? 我们将这种情况称为“ 回调”。 也就是说,目标代码的 API 接受一个指向 function 的指针(好的,一个引用)。它会记住 function,并随后在某些 UI 事件发生后的适当时间调用它。 该语法可能让您感到困惑——它解释了目标代码中缺少()
的原因。
学术诚信
您显然正在学习这项作业中有关单元测试的一些知识,并且具有值得称赞的好奇心。 当你提交你的工作时,一定要包括对这个 SO 问题的引用。 只要您引用外部资源,大多数学校都乐于使用它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.